内存布局
理解 Rust 如何在内存中组织数据是编写高效代码的关键。
基本类型的大小
use std::mem;
fn main() {
println!("bool: {} bytes", mem::size_of::<bool>()); // 1
println!("i32: {} bytes", mem::size_of::<i32>()); // 4
println!("i64: {} bytes", mem::size_of::<i64>()); // 8
println!("f64: {} bytes", mem::size_of::<f64>()); // 8
println!("char: {} bytes", mem::size_of::<char>()); // 4
println!("&i32: {} bytes", mem::size_of::<&i32>()); // 8 (64位系统)
}
结构体布局
#[repr(C)] // 使用 C 语言的布局规则
struct Point {
x: i32, // 4 bytes
y: i32, // 4 bytes
} // 总共 8 bytes
#[repr(C)]
struct MixedStruct {
a: u8, // 1 byte
// 填充 3 bytes
b: u32, // 4 bytes
c: u16, // 2 bytes
// 填充 2 bytes
} // 总共 12 bytes(有对齐)
fn main() {
println!("Point: {} bytes", mem::size_of::<Point>());
println!("MixedStruct: {} bytes", mem::size_of::<MixedStruct>());
}
内存对齐
Rust 会自动添加填充(padding)以满足对齐要求:
struct Aligned {
a: u8, // 1 byte
// 填充 7 bytes
b: u64, // 8 bytes
c: u8, // 1 byte
// 填充 7 bytes
} // 24 bytes(不是 10 bytes!)
// 优化布局:将大字段放在前面
struct Optimized {
b: u64, // 8 bytes
a: u8, // 1 byte
c: u8, // 1 byte
// 填充 6 bytes
} // 16 bytes
枚举的布局
enum Message {
Quit, // 0 bytes
Move { x: i32, y: i32 }, // 8 bytes
Write(String), // 24 bytes
}
// 枚举大小 = 判别式(discriminant) + 最大变体的大小
// 约 32 bytes(24 + 8 判别式 + 对齐)
fn main() {
println!("Message: {} bytes", mem::size_of::<Message>());
}
零大小类型(ZST)
struct Unit;
struct Empty {}
fn main() {
println!("Unit: {} bytes", mem::size_of::<Unit>()); // 0
println!("Empty: {} bytes", mem::size_of::<Empty>()); // 0
println!("(): {} bytes", mem::size_of::<()>()); // 0
}
ZST 的优势:
- 不占用内存
- 编译器可以优化掉
3D 内存布局可视化
胖指针
某些类型的引用包含额外元数据:
fn main() {
// 普通引用:1 个指针
let x: i32 = 42;
let r: &i32 = &x;
println!("&i32: {} bytes", mem::size_of_val(&r)); // 8
// 切片引用:指针 + 长度
let arr = [1, 2, 3, 4, 5];
let slice: &[i32] = &arr;
println!("&[i32]: {} bytes", mem::size_of_val(&slice)); // 16
// trait 对象:指针 + vtable 指针
let b: &dyn std::fmt::Debug = &42;
println!("&dyn Debug: {} bytes", mem::size_of_val(&b)); // 16
}
表示优化
Option<&T> 优化
fn main() {
println!("Option<&i32>: {} bytes",
mem::size_of::<Option<&i32>>()); // 8(不是 16!)
println!("&i32: {} bytes",
mem::size_of::<&i32>()); // 8
}
Rust 利用引用不能为 NULL 的特性,用 NULL 表示 None。
非零类型
use std::num::NonZeroU32;
fn main() {
println!("Option<u32>: {} bytes",
mem::size_of::<Option<u32>>()); // 8
println!("Option<NonZeroU32>: {} bytes",
mem::size_of::<Option<NonZeroU32>>()); // 4
}
内存布局属性
// 使用 C 布局
#[repr(C)]
struct CLayout {
a: u8,
b: u32,
}
// 紧凑布局(移除填充)
#[repr(packed)]
struct Packed {
a: u8,
b: u32,
} // 5 bytes(危险:未对齐访问)
// 透明布局(单字段)
#[repr(transparent)]
struct Wrapper(u32);
实践建议
✅ 推荐做法
// 1. 将大字段放在前面以减少填充
#[repr(C)]
struct Optimized {
large: u64, // 8 bytes
medium: u32, // 4 bytes
small: u8, // 1 byte
// 只需填充 3 bytes
}
// 2. 使用 #[repr(C)] 与 C 互操作
#[repr(C)]
struct FFIStruct {
x: i32,
y: i32,
}
// 3. 检查大小和对齐
const _: () = assert!(mem::size_of::<MyStruct>() == 16);
❌ 避免做法
// ❌ 不必要的 #[repr(packed)]
#[repr(packed)]
struct Bad {
a: u32, // 未对齐访问可能很慢
}
// ❌ 忽略内存布局
// 在性能关键的代码中应该考虑布局
struct Unoptimized {
a: u8, // 1 byte
b: u64, // 填充 7 bytes
c: u8, // 填充 7 bytes
} // 浪费 14 bytes
小结
- ✅ 不同类型有不同的内存大小
- ✅ 结构体会有对齐填充
- ✅ 优化字段顺序可以减少内存使用
- ✅ ZST 不占用内存
- ✅ Rust 对某些类型做了表示优化
- ✅ 使用
mem::size_of检查大小