Ch 1: std::mem - 内存操作利器
std::mem 是 Rust 标准库中处理内存操作的核心模块,提供了大量用于操作内存的实用函数。熟练掌握这些函数可以让代码更加简洁高效,同时避免不必要的内存分配和拷贝。本章将详细讲解 take、replace、size_of、transmute、forget 和 ManuallyDrop 等常用函数。
2. take - 取出值并替换为默认值
Section titled “2. take - 取出值并替换为默认值”take 方法用于取出 T 类型的值,并将该位置替换为 T 的默认值。
方法签名:
pub const fn take(this: &mut T) -> Twhere T: Default,示例:
use std::mem;
fn main() { let mut x = 5; let old_x = mem::take(&mut x);
println!("old_x = {}", old_x); // old_x = 5 println!("x = {:?}", x); // x = 0 (i32的默认值)}对于自定义类型,需要实现 Default trait:
use std::mem;
#[derive(Debug, Default)]struct Config { timeout: u64, retries: u32,}
fn main() { let mut config = Config { timeout: 30, retries: 3, };
let default_config = mem::take(&mut config);
println!("取出: {:?}", default_config); // Config { timeout: 30, retries: 3 } println!("现在: {:?}", config); // Config { timeout: 0, retries: 0 }}3. replace - 取出值并替换为新值
Section titled “3. replace - 取出值并替换为新值”replace 与 take 类似,但可以指定替换的新值,而不是默认值。
方法签名:
pub const fn replace(this: &mut T, replacement: T) -> T示例:
use std::mem;
fn main() { let mut x = String::from("hello"); let old_x = mem::replace(&mut x, String::from("world"));
println!("old_x = {}", old_x); // old_x = hello println!("x = {}", x); // x = world}replace 的一个常见用法是配合 Option:
use std::mem;
fn main() { let mut maybe_value: Option<String> = Some(String::from("hello"));
// 取出 Some 中的值,替换为 None let value = mem::replace(&mut maybe_value, None);
println!("value = {:?}", value); // value = Some("hello") println!("maybe_value = {:?}", maybe_value); // maybe_value = None}4. size_of - 获取类型大小
Section titled “4. size_of - 获取类型大小”size_of 返回类型的字节大小。
方法签名:
pub const fn size_of<T>() -> usize示例:
use std::mem;
fn main() { println!("i32 大小: {} 字节", mem::size_of::<i32>()); // 4 字节 println!("i64 大小: {} 字节", mem::size_of::<i64>()); // 8 字节 println!("bool 大小: {} 字节", mem::size_of::<bool>()); // 1 字节 println!("() 大小: {} 字节", mem::size_of::<()>()); // 0 字节
// 指针大小(依赖平台,通常是 64 位系统为 8 字节) println!("&i32 大小: {} 字节", mem::size_of::<&i32>()); // 8 字节}size_of_val - 获取值的实际大小:
use std::mem;
fn main() { let s = "hello"; let slice: &[i32] = &[1, 2, 3];
println!("字符串字面量大小: {} 字节", mem::size_of_val(s)); // 6 字节(含终止符) println!("[i32; 3] 大小: {} 字节", mem::size_of_val(slice)); // 12 字节}5. transmute - 类型转换
Section titled “5. transmute - 类型转换”transmute 是危险的类型转换函数,将一段内存按位解释为另一种类型。使用不当会导致未定义行为。
方法签名:
pub const unsafe fn transmute<T, U>(e: T) -> U示例:将 [u8; 4] 转换为 u32:
use std::mem;
fn main() { let bytes: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
// 安全的用法:大小相同的类型间的转换 let num: u32 = unsafe { mem::transmute(bytes) };
println!("u32: 0x{:08x}", num); // 小端序输出}注意: transmute 要求源类型和目标类型的大小完全相同:
use std::mem;
// 编译错误:大小不同// let num: u64 = unsafe { mem::transmute::<[u8; 4], u64>([0; 4]) };// error: cannot transmute between types of different sizes6. forget - 阻止值被 drop
Section titled “6. forget - 阻止值被 drop”forget 阻止值的析构函数被调用,使值”泄漏”。这在某些场景下是有用的,比如与 FFI 交互时。
方法签名:
pub const fn forget<T>(x: T)示例:
use std::mem;
struct HasDestructor(&'static str);
impl Drop for HasDestructor { fn drop(&mut self) { println!("Dropping: {}", self.0); }}
fn main() { let x = HasDestructor("hello");
println!("Creating and forgetting..."); mem::forget(x);
println!("Function ends..."); // 注意:这里不会打印 "Dropping: hello"}使用场景 - 泄漏计数器:
use std::mem;
fn main() { // 有时需要"泄漏"值以维持其静态 lifetime let _leaked = Box::new(42u32); // Box 不会被 drop,但值仍然有效(只是无法访问)}7. ManuallyDrop - 手动控制的析构
Section titled “7. ManuallyDrop - 手动控制的析构”ManuallyDrop 是一个包装器,用于手动控制值的析构时机。
方法签名:
pub struct ManuallyDrop<T> { value: T,}示例:
use std::mem::ManuallyDrop;
struct Droppable(&'static str);
impl Drop for Droppable { fn drop(&mut self) { println!("Dropping: {}", self.0); }}
fn main() { let mut x = ManuallyDrop::new(Droppable("hello"));
// 不会自动 drop println!("x = {:?}", x.value.0);
// 手动触发 drop unsafe { ManuallyDrop::drop(x); }
println!("After manual drop");}在结构体中安全持有裸指针:
use std::mem::ManuallyDrop;
struct Wrapper { data: ManuallyDrop<String>, // 一些其他字段...}
fn main() { let w = Wrapper { data: ManuallyDrop::new(String::from("hello")), };
// data 不会被意外 drop println!("{}", w.data);}8. 需要注意的点
Section titled “8. 需要注意的点”-
take和replace需要Default实现:对于自定义类型,必须实现Defaulttrait 才能使用take。 -
transmute是 unsafe 的:使用不当会导致:- 未定义行为
- 内存安全问题
- 违反 Rust 的别名规则
-
forget导致内存泄漏:虽然不推荐在普通代码中使用,但它不会造成内存错误,只是析构函数不会被调用。 -
ManuallyDrop是安全的替代方案:如果需要控制析构时机,优先使用ManuallyDrop而不是forget。 -
size_of是编译期求值的:mem::size_of::<T>()在编译时就确定,可在 const 上下文中使用。
9. 实践练习
Section titled “9. 实践练习”- 编写一个函数,使用
mem::take实现一个”重置”功能。 - 使用
mem::replace交换两个变量的值(不使用临时变量)。 - 研究
mem::zeroed的用法(注意其危险性)。 - 实现一个简单的内存池,使用
ManuallyDrop管理固定大小的缓冲区。
10. 总结
Section titled “10. 总结”std::mem 模块提供了 Rust 中最核心的内存操作原语:
| 函数 | 作用 | 安全性 |
|---|---|---|
take | 取出值并替换为默认值 | 安全 |
replace | 取出值并替换为新值 | 安全 |
size_of | 获取类型大小 | 安全 |
transmute | 位模式类型转换 | unsafe |
forget | 阻止析构 | 安全(但会泄漏) |
ManuallyDrop | 手动析构控制 | 安全 |
掌握这些函数,能让你在编写底层和高性能代码时更加得心应手。