一、自定义数据类型概述
专业名词释义:
- 代数数据类型(Algebraic Data Types,ADT):Rust 中结构体(产品类型,Product Type)和枚举(和类型,Sum Type)的统称,能精确表达"AND"和"OR"逻辑,是 Rust 类型系统最强大的特性之一。
- 结构体(Struct):具名/元组/单元三种形式,用于组合多个字段。
- 枚举(Enum):由多个互斥变体(Variant)组成,每个变体可携带数据。
- 类型别名(Type Alias) :使用
type关键字为已有类型赋予新名称,不创建新类型。 - 联合(Union):C 风格的联合体(本章暂不详解,后续内存布局章节讲解)。
用法:当原生类型无法描述业务实体(如用户、事件、网络地址)时,必须使用自定义类型。
注意事项与最佳实践:
- 自定义类型是 Rust "零成本抽象 + 内存安全"的基石,结合所有权系统后,编译器能静态消除大部分内存错误。
- 推荐始终添加
#[derive(Debug, Clone, PartialEq)]以便调试和比较。 - 枚举比结构体更适合"状态机"或"错误处理"(如
Result、Option就是枚举)。 - 类型别名仅提升可读性,不提供类型安全(区别于"新类型模式"------使用元组结构体包装)。
二、结构体(分类与初始化)
专业名词释义:
- 具名结构体(Named Struct):字段带名称,类似 C 的 struct。
- 元组结构体(Tuple Struct):字段无名称,仅保留类型顺序,常用于"新类型模式"(Newtype Pattern)。
- 单元结构体(Unit Struct):无字段,零大小类型(Zero-Sized Type,ZST),不占用内存。
用法示例:
rust
// 具名结构体
struct User {
active: bool,
user_id: u32,
scores: u8,
rank: char,
}
// 元组结构体(新类型模式,防止单位混用)
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
// 单元结构体(标记类型)
struct AlwaysEqual;
初始化与更新语法:
rust
// 基础初始化 + 修改
let mut user = User {
active: true,
user_id: 5678,
scores: 85,
rank: 'B',
};
user.scores = 90;
// 字段名与变量名相同时的简写语法
let scores = 100;
let rank = 'A';
let mut user = User {
active: true,
user_id: 5678,
scores, // 简写
rank, // 简写
};
// 更新语法(保留大部分字段,仅修改部分)
let user1 = User { active: true, user_id: 5678, scores: 85, rank: 'B' };
let user2 = User {
user_id: 5679,
..user1 // 结构体更新语法
};
注意事项与最佳实践:
- 具名结构体适合复杂业务实体,字段访问使用
.field。 - 元组结构体常用于类型安全包装(如
struct Meters(u32);防止与Seconds混用)。 - 单元结构体不占内存,常用作
PhantomData<T>或 trait 标记。 - 初始化必须提供所有字段(除非使用
..更新语法)。 - 深度提示 :结构体默认内存布局由编译器决定(
repr(Rust)),生产环境若需与 C 互操作,可加#[repr(C)]。 - 最佳实践:超过 3 个字段优先使用具名结构体;字段顺序影响内存对齐,必要时手动添加
#[repr(align(8))]。
三、枚举(分类、判别式与初始化)
专业名词释义:
- 有字段枚举:每个变体可携带具名或元组数据(类似结构体)。
- 无字段枚举:纯标签(C-like enum)。
- 判别式(Discriminant):整数标签,用于运行时区分不同变体,默认从 0 开始自增,可显式指定。
用法示例:
rust
// 有字段枚举
enum Animal {
Dog(i32, f64), // 元组风格
Cat { name_id: u32, weight: f64 }, // 具名风格
}
// 无字段枚举
enum Fieldless {
Tuple(),
Struct {},
Unit,
}
// 带判别式的枚举
enum Fo {
Bar = 123,
Baz = 456,
Quux, // 自动 457
}
判别式访问与自增规则:
rust
assert_eq!(0, Enum::Foo as isize);
assert_eq!(123, Fo::Bar as isize);
// 使用 mem::discriminant 比较(无需匹配)
use std::mem;
assert_eq!(
mem::discriminant(&Foo::A('a')),
mem::discriminant(&Foo::A('b'))
);
初始化示例:
rust
enum WebEvent {
PageLoad,
PageUnload,
KeyPress(char),
Click { x: i64, y: i64 },
}
let pressed = WebEvent::KeyPress('x');
let click = WebEvent::Click { x: 20, y: 80 };
枚举与结构体的相互嵌套:
rust
// 枚举作为结构体成员
struct IpAddr {
kind: IpAddrKind,
id: u32,
}
// 结构体作为枚举成员
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
注意事项与最佳实践:
- 枚举本质上是"带标签的联合体"(Tagged Union),内存占用 = 判别式大小 + 最大变体数据大小。
- 无字段枚举可直接
as isize转换;有字段枚举必须用mem::discriminant或match。 - 自增规则:未指定时从 0 开始 +1;显式指定后后续变体继续递增。
- 深度提示 :枚举是 Rust 错误处理(
Result)和可选值(Option)的核心,强烈推荐用于状态机。 - 最佳实践:变体超过 3 个时考虑拆分成多个枚举;复杂数据放在枚举内部而非外部结构体。
- 生产中常搭配
#[derive(Debug)]+match表达式进行穷尽匹配(编译器会强制检查)。
四、类型别名
专业名词释义:
- 类型别名(Type Alias) :使用
type关键字创建的同义词,编译后完全等价于原类型,不引入新类型检查。
用法示例:
rust
// 简化基本类型
type Kilometers = i32;
// 简化复杂枚举
enum VeryVerboseEnumOfThingsToDoWithNumbers {
Add,
Subtract,
}
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;
let x: Operations = Operations::Add;
注意事项与最佳实践:
- 类型别名仅提升可读性(长类型 → 业务语义),不提供额外类型安全。
- 与"新类型模式"(元组结构体)区别:
type Km = i32;可与i32直接相加;struct Km(i32);则禁止直接相加(更安全)。 - 常用于:复杂泛型别名(如
type Result<T> = std::result::Result<T, MyError>;)或业务领域名称。 - 深度提示:别名在泛型和 trait bound 中非常强大,但不要过度使用以免降低代码清晰度。
五、本章小结 + 进阶练习
学完本章你应该能做到:
- 熟练掌握结构体三种分类及初始化/更新语法
- 掌握枚举的分类、判别式规则、初始化及与结构体的嵌套使用
- 正确使用类型别名提升代码可读性,并区分其与新类型模式的差异
进阶练习(建议立刻敲代码):
- 定义一个
struct User并使用更新语法创建"升级版"实例。 - 实现一个带字段的枚举
WebEvent,编写match处理所有变体。 - 用类型别名简化一个复杂枚举,再用元组结构体实现"新类型"版本对比安全性。
- 尝试为枚举添加
#[repr(u8)]观察内存变化(使用std::mem::size_of)。
(完)