Rust 枚举类

带数据的枚举、`Option`、`Result`、以及 `match` 解构。

Intermediate
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.00

This 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
None

Option<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.

MethodEffect
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.

Friends & Partners