Rust 的内存布局设计强调效率、安全性和明确性,其布局规则由类型定义和编译器优化共同决定。以下是对 Rust 各类对象内存布局的详细分析:
一、基础类型布局
标量类型:
- 整数:
i32
/u32
占 4 字节,对齐 4 字节 - 浮点数:
f64
占 8 字节,对齐 8 字节 - 布尔值:
bool
占 1 字节(0x00
或0x01
) - 字符:
char
占 4 字节(UTF-8 编码)
- 整数:
复合类型:
- **元组
(T1, T2)
**:字段顺序排列,按最大对齐要求填充。1
(i32, u8) // 布局:[i32][u8][填充3字节] → 总大小8字节
- **数组
[T; N]
**:连续存储,无填充。1
[u16; 3] // 布局:[u16][u16][u16] → 总大小6字节
- **元组
二、结构体(Struct)布局
1. 默认布局(Rust布局)
- 字段顺序:编译器可能重排字段以最小化填充。
- 对齐:按字段中最大对齐要求对齐。
- 示例:
1
2
3
4
5
6struct RustStruct {
a: u8, // 1字节
b: u32, // 4字节(对齐4)
c: u16, // 2字节
}
// 优化后布局:[b(4)][c(2)][a(1)][填充1] → 总大小8字节
2. #[repr(C)]
布局
- 字段顺序:严格按声明顺序排列。
- 对齐:同 C 语言规则。
- 示例:
1
2
3
4
5
6
7
struct CStruct {
a: u8, // 1字节
b: u32, // 4字节(对齐4)
c: u16, // 2字节
}
// 布局:[a(1)][填充3][b(4)][c(2)] → 总大小12字节
3. 特殊布局属性
- **
#[repr(packed)]
**:取消填充(可能牺牲性能)。 - **
#[repr(align(n))]
**:强制对齐到n
字节。
三、枚举(Enum)布局
1. 带数据枚举
- 标签联合(Tagged Union):
- 标签(Discriminant):标识当前变体(通常为
usize
)。 - 联合体(Union):存储变体数据。
- 标签(Discriminant):标识当前变体(通常为
- 示例:
1
2
3
4
5
6enum MyEnum {
A, // 无数据
B(u32), // 包含u32
C { x: u8, y: u8 } // 包含结构
}
// 布局:[标签][联合体] → 标签大小 + 最大变体大小
2. 优化策略
- 空指针优化:
Option<&T>
中None
用0
表示,无需额外标签。 - 标签压缩:尝试用最小整数存储标签。
- 变体数据内联:小数据类型直接内联。
3. #[repr(u8)]
等
- 显式指定标签类型:
1
2
3
4
5
enum HttpStatus {
Ok = 200,
NotFound = 404,
}
四、动态类型与智能指针
**切片(Slice)
&[T]
**:- 胖指针:
[指针, 长度]
→ 16 字节(64位系统)。 - 内存连续:元素紧密排列。
- 胖指针:
**字符串
&str
**:- 同切片布局:
[指针, 长度]
。
- 同切片布局:
**
Box<T>
**:- 瘦指针:指向堆内存 → 8 字节(64位系统)。
- 内存布局:堆上存储
T
的值。
**
Vec<T>
**:- 三元组:
[指针, 长度, 容量]
→ 24 字节(64位系统)。 - 堆内存:连续存储元素。
- 三元组:
**特征对象
&dyn Trait
**:- 胖指针:
[数据指针, vtable指针]
→ 16 字节。 - vTable:包含方法指针、类型大小和对齐信息。
- 胖指针:
五、零大小类型(ZST)
- 特点:大小=0,对齐=1。
- 示例:
struct Nothing;
、()
。 - 优化:编译器消除ZST的实际内存分配。
六、动态大小类型(DST)
- 特点:编译期大小未知(如
[T]
、dyn Trait
)。 - 使用限制:必须通过指针(如
&[T]
、Box<dyn Trait>
)间接访问。
七、内存对齐控制
- 对齐规则:
1
2
3
4struct AlignExample {
a: u8, // 对齐1
b: u32, // 对齐4 → 结构体对齐=4
} - 手动对齐:
1
2
struct ForceAligned(u32); // 强制对齐到8字节
八、查看内存布局的工具
std::mem
函数:1
2std::mem::size_of::<T>() // 类型大小
std::mem::align_of::<T>() // 对齐要求- **
#[derive(Debug)]
**:结合调试器查看内存。 - 第三方库:如
std::mem::transmute
(谨慎使用)、bytemuck
。
总结表
类型 | 布局特点 | 示例大小(64位) |
---|---|---|
标量类型 | 紧密排列,无填充 | i32 : 4字节 |
默认结构体 | 字段重排优化填充 | 优化后可能≤12字节 |
#[repr(C)] 结构体 |
严格声明顺序,可能填充 | 可能12字节 |
枚举 | 标签联合 + 优化策略 | 通常8-24字节 |
切片 &[T] |
胖指针(数据指针 + 长度) | 16字节 |
Box<T> |
瘦指针(指向堆数据) | 8字节 |
Vec<T> |
三元组(指针 + 长度 + 容量) | 24字节 |
特征对象 &dyn T |
胖指针(数据指针 + vTable指针) | 16字节 |
ZST | 不占实际空间 | 0字节 |
通过理解这些规则,您可以预测数据的内存占用、优化性能,并安全地进行FFI交互。Rust的布局设计在保证安全的同时,尽可能追求高效率。