枚举

  • 参考文档:The Rust Programming Language

  • enum是一组变体的类型,每个变体可以携带不同类型与数量的数据,不是C的整数别名

    rust
    enum Message {
        Quit,                       // 不携带数据
        Move { x: i32, y: i32 },    // 携带结构体字段
        Write(String),              // 携带一个值
        ChangeColor(i32, i32, i32), // 携带元组
    }
  • 与java枚举的对比:java枚举是固定几个实例的类,Rust枚举的每个变体每次出现都能装不同的值,表达力接近密封类(sealed class)

  • 枚举搭配match解构是Rust组织数据的方式,替代了其他语言的继承与联合类型

模式匹配

  • match对一个值做多路分支,模式可以解构枚举、元组、结构体与区间

    rust
    fn describe(n: i32) -> i32 {
        match n {
            0 => 0,       // 字面量
            1..=9 => 1,   // 区间
            _ => -1,      // 通配符兜底
        }
    }
  • match 必须穷尽所有可能,漏掉变体会编译不通过,新增变体后所有match处编译器逐一报错,这是重构安全的来源

  • match是表达式,每个分支产生一个值,各分支类型必须一致

  • if let只处理单个模式,忽略其余情况;let else在模式不匹配时必须发散(提前return、panic或break)

    rust
    if let Some(x) = opt { }             // 只关心 Some 的情况
    let Some(x) = opt else { return; };  // 取出值,失败就提前返回
    

panic

  • panic!宏触发不可恢复错误:打印错误信息、展开栈并退出程序
  • 使用场景:程序自身的 bug(数组越界、断言失败、非法状态),而不是可预期的失败
  • 调试时用RUST_BACKTRACE=1环境变量查看完整调用栈

Option 与空值

  • Rust没有 null:空指针被称为"十亿美元错误",Rust把"可能没有值"编码进类型系统,用标准库枚举Option<T>表达

    rust
    enum Option<T> {
        Some(T),
        None,
    }
  • 编译器强制先从Some中取出值才能使用:想用T就必须处理None,空指针错误在编译期暴露而不是运行期崩溃

  • 直接取值的方法:unwrap()(是None就 panic)、expect("<信息>")(panic 时带上提示信息),适合原型与确信不为空的场景

  • 组合式惯用法:map、and_then、unwrap_or、ok_or,链式处理避免层层match

    rust
    let len = opt.map(|s| s.len()).unwrap_or(0);

Result 与错误传播

  • 可恢复错误用标准库枚举Result<T, E>表达,Rust没有异常机制,错误是普通值,会出现在函数签名里,调用者无法忽略

    rust
    enum Result<T, E> {
        Ok(T),
        Err(E),
    }
  • 与java的try/catch对比:异常可以不声明也不捕获,Result把失败路径写进类型,漏处理就是编译错误

  • 与Go的对比:Go每层都要手写if err != nil { return err },Rust的?把同样的检查与返回压缩成一个后缀字符

  • ?运算符:成功时取出Ok里的值,失败时立即return Err,是展开match的语法糖

    rust
    fn read_num(s: &str) -> Result<i32, std::num::ParseIntError> {
        let n: i32 = s.trim().parse()?; // 出错时直接把 Err 返回给调用者
        Ok(n * 2)
    }
  • ?会通过From自动把错误转换成目标类型,常用Box<dyn Error>承接各种错误,让main统一返回Result

    rust
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let n = read_num("42")?;
        println!("{}", n);
        Ok(())
    }
  • 取舍惯例:程序 bug 用panic!,调用方有能力处理的失败用Result,二者的边界写在代码里而不是靠约定