元组、结构体和枚举是 Rust 组织数据的三种基本形状。这篇不重复语法,而是用真实输出回答:它们各占多少内存、编译器会不会偷偷重排字段、枚举的「标签」要付出多少空间,以及为什么不能像访问字段一样访问枚举里的数据。
源码:
1 | use std::mem::size_of;
2 | fn main() {
3 | let t = (500, 6.4, 'z');
4 | let (a, b, c) = t; // 解构
5 | println!("t.0 = {}, t.1 = {}, t.2 = {}", t.0, t.1, t.2);
6 | println!("解构:a = {a}, b = {b}, c = {c}");
7 | println!("(i32, f64, char) 占 {} 字节,字段字节数之和 = {}", size_of::<(i32, f64, char)>(), size_of::<i32>() + size_of::<f64>() + size_of::<char>());
8 | let unit = ();
9 | println!("单元类型 () = {:?},占 {} 字节", unit, size_of::<()>());
10 | let one = (1,); // 单元素元组要带逗号
11 | println!("(1,) = {:?},(1) 是普通括号表达式 = {}", one, (1));
12 | }
编译:成功
运行(退出码 0):
t.0 = 500, t.1 = 6.4, t.2 = z
解构:a = 500, b = 6.4, c = z
(i32, f64, char) 占 16 字节,字段字节数之和 = 16
单元类型 () = (),占 0 字节
(1,) = (1,),(1) 是普通括号表达式 = 1 源码:
1 | use std::mem::size_of;
2 | #[derive(Debug, Clone)] struct User { name: String, age: u8, active: bool }
3 | struct Point(f64, f64); // 元组结构体
4 | struct Marker; // 单元结构体
5 | struct Loose { a: u8, b: u32, c: u8 }
6 | #[repr(C)] struct LooseC { a: u8, b: u32, c: u8 }
7 | fn main() {
8 | let u1 = User { name: "ann".into(), age: 30, active: true };
9 | let u2 = User { name: "bob".into(), ..u1.clone() }; // 结构体更新语法
10 | println!("{:?}\n{:?}", u1, u2);
11 | let p = Point(1.0, 2.5); println!("Point.0 = {}, Point.1 = {}", p.0, p.1);
12 | println!("Marker 占 {} 字节", size_of::<Marker>());
13 | println!("Loose{{u8,u32,u8}} 默认布局:{} 字节;#[repr(C)]:{} 字节;字段之和 = 6", size_of::<Loose>(), size_of::<LooseC>());
14 | }
编译:成功
运行(退出码 0):
User { name: "ann", age: 30, active: true }
User { name: "bob", age: 30, active: true }
Point.0 = 1, Point.1 = 2.5
Marker 占 0 字节
Loose{u8,u32,u8} 默认布局:8 字节;#[repr(C)]:12 字节;字段之和 = 6字段重排的规则与 #[repr(C)] 的区别见Struct 对齐与 Padding。
源码:
1 | use std::mem::size_of;
2 | #[derive(Debug)]
3 | enum Shape { Circle(f64), Rect { w: f64, h: f64 }, Empty }
4 | #[derive(Debug, Clone, Copy)] enum Color { Red, Green = 10, Blue }
5 | fn area(s: &Shape) -> f64 { match s { Shape::Circle(r) => 3.14 * r * r, Shape::Rect { w, h } => w * h, Shape::Empty => 0.0 } }
6 | fn main() {
7 | for s in [Shape::Circle(2.0), Shape::Rect { w: 3.0, h: 4.0 }, Shape::Empty] { println!("{:?} 面积 = {}", s, area(&s)); }
8 | println!("size_of::<Shape>() = {} 字节(最大变体 Rect = 16 字节 + 标签)", size_of::<Shape>());
9 | println!("Color 是无数据枚举:size_of = {} 字节;Red = {},Green = {},Blue = {}", size_of::<Color>(), Color::Red as i32, Color::Green as i32, Color::Blue as i32);
10 | println!("size_of::<Option<u8>>() = {};size_of::<Option<u32>>() = {};size_of::<Option<f64>>() = {}", size_of::<Option<u8>>(), size_of::<Option<u32>>(), size_of::<Option<f64>>());
11 | }
编译:成功
运行(退出码 0):
Circle(2.0) 面积 = 12.56
Rect { w: 3.0, h: 4.0 } 面积 = 12
Empty 面积 = 0
size_of::<Shape>() = 24 字节(最大变体 Rect = 16 字节 + 标签)
Color 是无数据枚举:size_of = 1 字节;Red = 0,Green = 10,Blue = 11
size_of::<Option<u8>>() = 2;size_of::<Option<u32>>() = 8;size_of::<Option<f64>>() = 16枚举的内存布局(标签放在哪、niche 优化)见Enum 的内存布局与Option<&T> 的空指针优化。
源码:
1 | enum Shape { Circle(f64), Empty }
2 | fn main() {
3 | let s = Shape::Circle(1.0);
4 | println!("{}", s.0);
5 | }
编译:失败
error[E0609]: no field `0` on type `Shape`
--> <实验目录>/src/u7d.rs:4:22
|
4 | println!("{}", s.0);
| ^ unknown field
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0609`.取出枚举里的数据靠match 的穷尽匹配。