Skip to content

Ch 1: std::mem - 内存操作利器

std::mem 是 Rust 标准库中处理内存操作的核心模块,提供了大量用于操作内存的实用函数。熟练掌握这些函数可以让代码更加简洁高效,同时避免不必要的内存分配和拷贝。本章将详细讲解 take、replace、size_of、transmute、forget 和 ManuallyDrop 等常用函数。

take 方法用于取出 T 类型的值,并将该位置替换为 T 的默认值。

方法签名:

pub const fn take(this: &mut T) -> T
where
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 }
}

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
}

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 字节
}

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 sizes

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,但值仍然有效(只是无法访问)
}

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);
}
  1. take 和 replace 需要 Default 实现:对于自定义类型,必须实现 Default trait 才能使用 take。

  2. transmute 是 unsafe 的:使用不当会导致:

    • 未定义行为
    • 内存安全问题
    • 违反 Rust 的别名规则
  3. forget 导致内存泄漏:虽然不推荐在普通代码中使用,但它不会造成内存错误,只是析构函数不会被调用。

  4. ManuallyDrop 是安全的替代方案:如果需要控制析构时机,优先使用 ManuallyDrop 而不是 forget。

  5. size_of 是编译期求值的:mem::size_of::<T>() 在编译时就确定,可在 const 上下文中使用。

  1. 编写一个函数,使用 mem::take 实现一个”重置”功能。
  2. 使用 mem::replace 交换两个变量的值(不使用临时变量)。
  3. 研究 mem::zeroed 的用法(注意其危险性)。
  4. 实现一个简单的内存池,使用 ManuallyDrop 管理固定大小的缓冲区。

std::mem 模块提供了 Rust 中最核心的内存操作原语:

函数作用安全性
take取出值并替换为默认值安全
replace取出值并替换为新值安全
size_of获取类型大小安全
transmute位模式类型转换unsafe
forget阻止析构安全(但会泄漏)
ManuallyDrop手动析构控制安全

掌握这些函数,能让你在编写底层和高性能代码时更加得心应手。