Rust 枚举类
带数据的枚举、`Option`、`Result`、以及 `match` 解构。
Key PointsRust 的枚举每个变体可以带不同类型的数据,等价于「带标签的联合体」。
基本枚举
Rust
#[derive(Debug)]
enum Direction {
North,
South,
East,
West,
}
fn main() {
let d = Direction::North;
println!("{:?}", d);
match d {
Direction::North => println!("向北"),
Direction::South => println!("向南"),
Direction::East => println!("向东"),
Direction::West => println!("向西"),
}
}Output
North
向北带数据的变体
Rust
#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Rect(f64, f64),
Triangle { base: f64, height: f64 },
Point,
}
fn area(s: &Shape) -> f64 {
match s {
Shape::Circle { radius } => 3.14159 * radius * radius,
Shape::Rect(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
Shape::Point => 0.0,
}
}
fn main() {
println!("{:.2}", area(&Shape::Circle { radius: 1.0 }));
println!("{:.2}", area(&Shape::Rect(3.0, 4.0)));
println!("{:.2}", area(&Shape::Triangle { base: 6.0, height: 2.0 }));
println!("{:.2}", area(&Shape::Point));
}Output
3.14
12.00
6.00
0.00This is where Rust enums shine: each variant carries its own data, destructured in match. Doing the same with inheritance means four subclasses plus a virtual method, and a real risk of forgetting one.
Option
Rust
fn find_even(v: &[i32]) -> Option<i32> {
v.iter().copied().find(|x| x % 2 == 0)
}
fn main() {
let v = vec![1, 3, 4, 5];
match find_even(&v) {
Some(n) => println!("找到偶数 {}", n),
None => println!("没有偶数"),
}
// 常用组合子
println!("{:?}", find_even(&v).map(|x| x * 10));
println!("{:?}", find_even(&v).unwrap_or(0));
println!("{:?}", find_even(&[1, 3]));
}Output
找到偶数 4
Some(40)
4
NoneOption<T> is literally enum Option<T> { Some(T), None }. Rust uses it instead of null, so the type system forces you to handle absence instead of crashing at runtime.
| Method | Effect |
|---|---|
map | 有值时转换 |
and_then | 有值时再返回一个 Option |
unwrap_or | 有值取值,否则用默认 |
unwrap_or_else | 默认值懒计算 |
ok_or | 转成 Result |
is_some / is_none | 判断 |
take / replace | 取出或替换 |
Result
Rust
fn parse_age(s: &str) -> Result<u32, String> {
match s.parse::<u32>() {
Ok(n) if n < 150 => Ok(n),
Ok(n) => Err(format!("年龄 {} 不合理", n)),
Err(_) => Err(format!("'{}' 不是数字", s)),
}
}
fn main() {
println!("{:?}", parse_age("28"));
println!("{:?}", parse_age("200"));
println!("{:?}", parse_age("abc"));
}Output
Ok(28)
Err("年龄 200 不合理")
Err("'abc' 不是数字")C 风格枚举
Rust
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum Status {
Pending = 0,
Paid = 1,
Cancelled = 2,
}
fn main() {
println!("{}", Status::Paid as u8);
let s = Status::from_u8(1);
println!("{:?}", s);
}
impl Status {
fn from_u8(v: u8) -> Option<Status> {
match v {
0 => Some(Status::Pending),
1 => Some(Status::Paid),
2 => Some(Status::Cancelled),
_ => None,
}
}
}Output
1
Some(Paid)Rust enums cannot be cast from an integer (unlike C's
(Status)1). You must write from_u8 or use a crate like num_enum — the cost and the benefit of safety.基本枚举
Rust
#[derive(Debug)]
enum Direction {
North,
South,
East,
West,
}
fn main() {
let d = Direction::North;
println!("{:?}", d);
match d {
Direction::North => println!("向北"),
Direction::South => println!("向南"),
Direction::East => println!("向东"),
Direction::West => println!("向西"),
}
}Output
North
向北带数据的变体
Rust
#[derive(Debug)]
enum Shape {
Circle { radius: f64 },
Rect(f64, f64),
Triangle { base: f64, height: f64 },
Point,
}
fn area(s: &Shape) -> f64 {
match s {
Shape::Circle { radius } => 3.14159 * radius * radius,
Shape::Rect(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
Shape::Point => 0.0,
}
}
fn main() {
println!("{:.2}", area(&Shape::Circle { radius: 1.0 }));
println!("{:.2}", area(&Shape::Rect(3.0, 4.0)));
println!("{:.2}", area(&Shape::Triangle { base: 6.0, height: 2.0 }));
println!("{:.2}", area(&Shape::Point));
}Output
3.14
12.00
6.00
0.00这是 Rust 枚举最有价值的地方:每个变体携带自己的数据,match 时解构出来。用类继承实现同样的东西要写四个子类加一个虚函数,还要担心忘记加新子类。
Option
Rust
fn find_even(v: &[i32]) -> Option<i32> {
v.iter().copied().find(|x| x % 2 == 0)
}
fn main() {
let v = vec![1, 3, 4, 5];
match find_even(&v) {
Some(n) => println!("找到偶数 {}", n),
None => println!("没有偶数"),
}
// 常用组合子
println!("{:?}", find_even(&v).map(|x| x * 10));
println!("{:?}", find_even(&v).unwrap_or(0));
println!("{:?}", find_even(&[1, 3]));
}Output
找到偶数 4
Some(40)
4
NoneOption<T> 就是 enum Option<T> { Some(T), None }。Rust 用它代替 null——类型系统强制你处理「没有值」的情况,而不是等运行时崩。
| 方法 | 作用 |
|---|---|
map | 有值时转换 |
and_then | 有值时再返回一个 Option |
unwrap_or | 有值取值,否则用默认 |
unwrap_or_else | 默认值懒计算 |
ok_or | 转成 Result |
is_some / is_none | 判断 |
take / replace | 取出或替换 |
Result
Rust
fn parse_age(s: &str) -> Result<u32, String> {
match s.parse::<u32>() {
Ok(n) if n < 150 => Ok(n),
Ok(n) => Err(format!("年龄 {} 不合理", n)),
Err(_) => Err(format!("'{}' 不是数字", s)),
}
}
fn main() {
println!("{:?}", parse_age("28"));
println!("{:?}", parse_age("200"));
println!("{:?}", parse_age("abc"));
}Output
Ok(28)
Err("年龄 200 不合理")
Err("'abc' 不是数字")C 风格枚举
Rust
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum Status {
Pending = 0,
Paid = 1,
Cancelled = 2,
}
fn main() {
println!("{}", Status::Paid as u8);
let s = Status::from_u8(1);
println!("{:?}", s);
}
impl Status {
fn from_u8(v: u8) -> Option<Status> {
match v {
0 => Some(Status::Pending),
1 => Some(Status::Paid),
2 => Some(Status::Cancelled),
_ => None,
}
}
}Output
1
Some(Paid)Rust 的枚举不能直接把整数转成变体(不像 C 的
(Status)1)。必须手写 from_u8 或引入 num_enum 之类的 crate。这是安全性的代价,也是它的好处。