Rust学习笔记:结构体与枚举——用Rust组织真正的业务数据
前几篇我们一直在和标量、字符串、函数打交道。实际业务中的数据远比这些复杂——用户信息、订单状态、配置项……这一篇学习 Rust 如何用结构体(struct)和枚举(enum)来组织有意义的复合数据。
本文所有代码均已实测通过。
1. 结构体:把相关数据打包
1.1 定义与实例化
struct User {
username: String,
email: String,
active: bool,
}
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
active: true,
};
// 访问字段
println!("{} <{}>", user.username, user.email);
和 C 的结构体很像,但 Rust 的 struct 是真正的类型,可以直接持有 String 而不用操心内存。
修改字段需要整个实例是 mut 的——Rust 没有「部分字段可变」:
let mut user2 = User {
username: String::from("bob"),
email: String::from("bob@example.com"),
active: false,
};
user2.active = true; // ✅ 整个 user2 是 mut
1.2 结构体所有权的注意事项
上面的 User 用了 String 而不是 &str,这意味着结构体自己拥有所有数据。如果用引用(&str),就需要标注生命周期——初学者先避开,就用 String。
2. impl:给结构体添加方法
impl 块是把函数「挂」到类型上的方式:
impl User {
// 关联函数(没有 self)——相当于静态方法、构造函数
fn new(username: &str, email: &str) -> User {
User {
username: String::from(username),
email: String::from(email),
active: true,
}
}
// 方法(&self)——只读访问
fn summary(&self) -> String {
format!("{} <{}>", self.username, self.email)
}
// 可变方法(&mut self)——修改自身
fn deactivate(&mut self) {
self.active = false;
}
}
let mut user = User::new("charlie", "charlie@example.com");
println!("{}", user.summary()); // charlie <charlie@example.com>
user.deactivate();
关键区分:
| 参数 | 含义 | 能否修改字段 |
|---|---|---|
&self |
不可变借用,只读 | ❌ |
&mut self |
可变借用,可修改 | ✅ |
self |
拿走所有权,消耗掉实例 | ✅(用完就没了) |
Rust 没有「类」的概念,struct + impl 就是它的面向对象。数据和行为分开声明,但逻辑上紧密结合。
3. 元组结构体:给元组起个名
有时候不需要给字段取名字,只想给一个元组赋予类型含义:
struct Color(i32, i32, i32);
struct Point(f64, f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0, 0.0);
println!("黑色: ({}, {}, {})", black.0, black.1, black.2);
注意 Color 和 Point 虽然内部结构相同(都是三个数值),但它们是完全不同的类型——不能把 Color 传给接受 Point 的函数。这就是元组结构体的价值:用类型系统表达语义,避免混淆。
4. 枚举:一个值可以是多种可能
Rust 的枚举比大多数语言强大得多——每个变体可以携带不同类型、不同数量的数据。
enum IpAddr {
V4(u8, u8, u8, u8), // 带四个 u8
V6(String), // 带一个 String
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
更复杂的例子——每个变体都可以有完全不同的数据形态:
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 命名字段
Write(String), // 元组风格
ChangeColor(i32, i32, i32), // 元组风格
}
结合 impl,枚举也可以有方法:
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({x}, {y})"),
Message::Write(text) => println!("写入: {text}"),
Message::ChangeColor(r, g, b) => println!("改色: ({r}, {g}, {b})"),
}
}
}
这个设计在模式匹配中威力巨大——编译器不仅检查穷尽性,还能通过模式解构直接提取每个变体携带的数据。
5. Option<T>:Rust 没有 null
Rust 没有 null。它用 Option<T> 枚举来表达「可能有值,也可能没有」:
enum Option<T> {
None,
Some(T),
}
Option<T> 是标准库预置的,不用自己定义,直接使用:
let some_number = Some(42);
let absent: Option<i32> = None;
解包 Option
match 解包:
match some_number {
Some(n) => println!("有值: {n}"),
None => println!("无值"),
}
if let 简化写法——只关心一个分支时:
if let Some(n) = some_number {
println!("解包: {n}");
}
快捷方法:
some_number.unwrap(); // 直接取值,None 会 panic(不安全!)
absent.unwrap_or(0); // 取值,None 时给默认值 0
生产代码中尽量用 match 或 unwrap_or 处理 Option,裸 unwrap() 只适合快速原型。
总结
这一篇覆盖了 Rust 组织业务数据的核心工具:
- 结构体——打包相关字段,
impl挂方法,关联函数做构造函数 - 元组结构体——给元组赋予类型语义,
Color和Point不能互换 - 枚举——每个变体可以带不同数据,配合
match穷尽解构 Option<T>——替代 null,编译器强制你处理「无值」的情况
结构体 + 枚举 + match 构成了 Rust 类型系统的表达核心。掌握这三样,大部分业务逻辑都能用类型安全的方式表达清楚。
下一篇进入集合类型:Vec(动态数组)和 HashMap(键值对),让数据真正「活」起来。

浙公网安备 33010602011771号