03 Rust复杂数据定义
3.1 Rust 结构体(Struct)核心概念与用法笔记
在 Rust 中,结构体(struct)用于创建自定义的数据类型,将多项相关联的值打包在一起。配合 impl 块(实现方法)和组合(Composition),你可以构建出结构清晰、职责分明的面向对象风格或函数式风格的代码架构。
一、 定义结构体(Struct Definitions)
Rust 支持三种形式的结构体,适应不同的数据组织场景:
1. 具名字段结构体(C-like Structs)
最常用的形式,每个字段都有明确的名字和类型。
##[derive(Debug)]
struct ServerConfig {
host: String,
port: u16,
timeout_seconds: u64,
}
2. 元组结构体(Tuple Structs)
字段没有名字,只有类型顺序。适合给基本类型起一个“强类型别名”,避免混淆。
struct Point(i32, i32);
struct Color(u8, u8, u8);
3. 单元结构体(Unit-like Structs)
没有任何字段,类似于 ()。通常用于实现某些特定特征(Trait)而不需要存储状态。
struct EmptyMarker;
二、 实现结构体中的方法(Methods & Associated Functions)
通过 impl 块,可以为结构体绑定函数。Rust 将其分为两类:
关联函数(Associated Functions / Constructors):类似其他语言的“静态方法”,通常用于构造实例(惯例命名为
new)。注意:它没有self参数。方法(Methods):需要操作具体实例的函数。它们的第一个参数必须是
self的某种形态(self、&self或&mut self)。
impl ServerConfig {
// 1. 关联函数:用于构造实例
fn new(host: String, port: u16) -> Self {
Self {
host,
port,
timeout_seconds: 30, // 默认超时时间
}
}
// 2. 方法(借用不可变实例):只读取数据
fn address(&self) -> String {
format!("{}:{}", self.host, self.port)
}
// 3. 方法(借用可变实例):修改内部状态
fn set_timeout(&mut self, timeout: u64) {
self.timeout_seconds = timeout;
}
}
使用示例:
fn main() {
// 调用关联函数创建实例(必须是 mut 才能调用可变方法)
let mut config = ServerConfig::new("127.0.0.1".to_string(), 8080);
println!("Server address: {}", config.address()); // 读取
config.set_timeout(60); // 修改
}
三、 通过组合产生新的结构体(Composition)
Rust 强烈推荐“组合优于继承”(Composition over Inheritance)。通过将已有的结构体作为新结构体的字段,可以轻松构建出更复杂、层级化的数据结构。
1. 包含具名字段的组合
当一个大系统由多个子模块配置组成时,直接组合:
##[derive(Debug)]
struct DatabaseConfig {
url: String,
max_connections: u32,
}
##[derive(Debug)]
struct AppConfig {
name: String,
server: ServerConfig, // 组合了前面定义的 ServerConfig
database: DatabaseConfig, // 组合了 DatabaseConfig
}
实例化与访问:
let app_cfg = AppConfig {
name: "MyService".to_string(),
server: ServerConfig::new("0.0.0.0".to_string(), 3000),
database: DatabaseConfig {
url: "postgres://localhost:5432/db".to_string(),
max_connections: 10,
},
};
// 通过点号链式访问内层结构体的字段
println!("Database max conn: {}", app_cfg.database.max_connections);
2. 字段初始化简写与结构体更新语法(Struct Update Syntax)
当局部变量名与字段名相同时,可以直接简写;同时可以用 .. 语法基于已有实例快速派生新实例:
let host = "192.168.1.100".to_string();
let port = 9000;
// 简写初始化
let base_config = ServerConfig {
host,
port,
timeout_seconds: 10,
};
// 结构体更新语法:复用 base_config 的 host 和 timeout,只修改 port
let custom_config = ServerConfig {
port: 8081,
..base_config
};
四、 核心优势总结
零开销抽象:Rust 的组合在内存中是平铺布局的(没有隐藏的虚表或指针开销),性能等价于 C 语言的手动内存布局。
清晰的借用控制:通过
&self、&mut self和self,编译器在编译期就能严格杜绝数据竞争和非法的状态修改。
3.2 Rust 枚举(Enum)核心概念与用法笔记
在 Rust 中,枚举(enum)远比传统语言(如 C/C++、Java)中的枚举强大。它们是代数数据类型(Algebraic Data Types),不仅能表示一组离散的值,还能携带丰富的数据,是构建强类型、安全架构的基石。
一、 枚举的三种形态(变体 Variants)
Rust 枚举的每个变体(Variant)都可以独立携带不同类型和结构的数据。主要分为三种形态:
1. 单元变体(Unit Variants)
不带任何数据,类似于传统的 C 风格枚举。适用于表示纯粹的状态或标签。
enum Direction {
North,
South,
East,
West,
}
2. 元组变体(Tuple Variants)
包含按位置排序的匿名数据,类似于元组。
enum Message {
Quit, // 类似单元变体
Move { x: i32, y: i32 }, // (注意:此处示例混淆了结构体,下面修正)
Write(String), // 包含一个 String
ChangeColor(i32, i32, i32), // 包含三个 i32
}
(修正上面的注释):
Write(String)和ChangeColor(i32, i32, i32)属于元组变体。
3. 结构体变体(Struct Variants)
包含具名字段,类似于标准的 struct。
enum WorkerCount {
Explicit(usize),
Ignored {
value: String,
used: usize
}, // 具名字段结构体变体
}
二、 如何构造与实例化枚举
带数据的枚举变体就像是一个可以传入参数并实例化的工厂或类型:
// 构造一个带有字段的 Ignored 变体实例
let count = WorkerCount::Ignored {
value: "invalid_env".to_string(),
used: 4,
};
三、 如何使用与模式匹配(Match)
Rust 强制使用 match 或 if let 对枚举进行穷尽式匹配(Exhaustive Matching),这保证了所有可能的分支都不会被漏掉。
match count {
WorkerCount::Explicit(n) => {
println!("使用用户指定的线程数: {n}");
}
WorkerCount::Ignored { value, used } => {
println!("环境变量格式错误: '{}', 已降级使用默认值: {}", value, used);
}
}
四、 Rust 枚举的两大王牌:Option 与 Result
Rust 标准库中最常用的两个泛型枚举:
Option<T>:用来替代其他语言中的null。
pub enum Option<T> {
Some(T),
None,
}
Result<T, E>:用来处理可恢复的错误。
pub enum Result<T, E> {
Ok(T),
Err(E),
}
五、 枚举的高级特性与优势
零成本抽象(Zero-cost abstractions):Rust 编译器会对枚举进行内存优化(例如利用指针不为零的特性,让
Option<NonZeroUsize>的内存大小和原始类型完全一致)。携带行为:可以通过
impl块为枚举定义方法、实现特征(Traits)。类型安全:将状态和与状态相关的数据绑定在一起,使得非法的状态组合在编译期就无法产生。