多种选择:枚举与匹配

原理

Rust 的 enum 是带标签的联合体(tagged union / sum type)。编译器在内部存储为:一个 discriminants(标签,通常是一个整数,取最小所需位数)+ 一个变体数据的 union。对于无数据变体,discriminants 本身足够。

match 在编译期做穷尽性检查(exhaustiveness checking)。编译器构建决策树(decision tree),为每个模式生成对应的跳转指令。对简单枚举,编译后等价于 switch 语句;对复杂模式,生成嵌套的条件分支。

Option<T> 是一个大小等于 max(sizeof(T), 0) + discriminants 的类型。None 没有运行时代价(使用特殊的 discriminants 值或 niche optimization)。例如 Option<bool> 占用 1 字节(而非 2 字节),因为 bool 只用 0/1,剩余空间可编码 None

match 守卫(if n > 5)在模式匹配之后执行,不改变穷尽性检查的范围。

C: 枚举与联合体


语法

定义

enum TrafficLight {
    Red,            // 无数据
    Yellow,
    Green,
}
 
enum Message {
    Quit,
    Move { x: i32, y: i32 },  // 带命名字段
    Write(String),            // 带元组数据
    ChangeColor(i32, i32, i32),
}

match 穷尽匹配

let light = TrafficLight::Red;
match light {
    TrafficLight::Red => "stop",
    TrafficLight::Yellow => "slow",
    TrafficLight::Green => "go",
    // 编译器强制穷尽所有变体
}
match msg {
    Message::Quit => { },
    Message::Move { x, y } => println!("({},{})", x, y),
    Message::Write(text) => println!("{}", text),
    Message::ChangeColor(r, g, b) => println!("{},{},{}", r, g, b),
}

Option

enum Option<T> { Some(T), None }
 
let x: Option<i32> = Some(5);
// let y = x + 1;        // 错误:Option<i32> != i32
 
let y = match x {
    Some(n) => n + 1,
    None => 0,
};
 
// 快捷方法
x.unwrap();         // panic on None
x.unwrap_or(0);     // None 时返回默认
x.is_some();
x.is_none();

if let — 单分支 match 简写

if let Some(n) = x {
    println!("有值:{}", n);
} else {
    println!("无值");
}

通配符 _ 和守卫

match n {
    1 => "one",
    2..=9 => "small",
    _ => "other",     // 匹配一切
}
 
match opt {
    Some(n) if n > 0 => "正数",
    Some(_) => "非正数",
    None => "无",
}

为枚举实现方法

impl TrafficLight {
    fn duration(&self) -> u32 {
        match self {
            Self::Red => 60,
            Self::Yellow => 5,
            Self::Green => 45,
        }
    }
}

实践

力扣问题

力扣: 力扣状态机题 — 状态机模式

enum State { Playing, Paused, Over }

力扣: 力扣数组计数题 — match 分支

AI 自检

  1. Option<bool> 占用几个字节?解释 niche optimization 原理。
  2. matchif let 在编译后的控制流图有什么区别?