从零开始学Rust:枚举(enum)与模式匹配核心机制

枚举类型本质

Rust的枚举(enum)本质上是一种 代数数据类型 (Algebraic Data Type),与函数式语言(F#OCaml/Haskell)中的实现类似。枚举允许定义包含不同数据类型的 变体(Variant),每个变体可携带不同类型和数量的关联数据。

Rust's enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.

There's another advantage to using an enum rather than a struct: each variant can have different types and amounts of associated data.

rust 复制代码
fn main() {
    enum IpAddr {
        V4(String),
        V6(String),
    }
    let home = IpAddr::V4(String::from("127.0.0.1"));
    let loopback = IpAddr::V6(String::from("::1"));
}
rust 复制代码
fn main() {
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
}

Option枚举与空值安全(control flow operator)

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}
  • Rust通过Option<T>枚举优雅处理空值问题,包含Some(T)None两个变体

  • 必须显式处理Option类型,避免 "Null References: The Billion Dollar Mistake"(Tony Hoare提出的空引用问题:"十亿美元错误")

    I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

  • Option类型保证不会为null,极大提高代码安全性

    rust 复制代码
    fn main() {
        let some_number = Some(5);
        let some_string = Some("a string");
        let absent_number: Option<i32> = None;
    }

You have to convert an Option<T> to a T before you can perform T operations with it. Generally, this helps catch one of the most common issues with null: assuming that something isn't null when it actually is.

Not having to worry about incorrectly assuming a not-null value helps you to be more confident in your code. In order to have a value that can possibly be null, you must explicitly opt in by making the type of that value Option<T>. Then, when you use that value, you are required to explicitly handle the case when the value is null. Everywhere that a value has a type that isn't an Option<T>, you can safely assume that the value isn't null. This was a deliberate design decision for Rust to limit null's pervasiveness and increase the safety of Rust code.

模式匹配机制

  • match表达式:

    • 必须穷尽所有可能模式(exhaustive matching)
    • 每个分支包含模式(pattern)和执行代码
    • 可解构枚举值获取内部数据
    rust 复制代码
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }

    Next are the match arms. An arm has two parts: a pattern and some code.

    rust 复制代码
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => {
                println!("Lucky penny!");
                1
            }
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }

    50 State Quarters

    rust 复制代码
    #[derive(Debug)]
    enum UsState {
        Alabama,
        Alaska,
        // --snip--
    }
    
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter(UsState),
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter(state) => {
                println!("State quarter from {:?}!", state);
                25
            }
        }
    }
    
    fn main() {
        value_in_cents(Coin::Quarter(UsState::Alaska));
    }
  • 通配模式:

    • _匹配所有未指定情况
    • 常用()单位值表示不执行操作
  • if let语法糖:

    • 简化单一模式匹配场景
    • 牺牲穷尽性检查换取代码简洁性
    • 等价于只处理一个分支的match表达式

Matches in Rust are exhaustive : we must exhaust every last possibility in order for the code to be valid. Especially in the case of Option<T>, when Rust prevents us from forgetting to explicitly handle the None case, it protects us from assuming that we have a value when we might have null, thus making the billion-dollar mistake discussed earlier impossible.

By putting it after our other arms, the _ will match all the possible cases that aren't specified before it. The () is just the unit value, so nothing will happen in the _ case. As a result, we can say that we want to do nothing for all the possible values that we don't list before the _ placeholder.

rust 复制代码
let some_u8_value = 0u8;
   match some_u8_value {
       1 => println!("one"),
       3 => println!("three"),
       5 => println!("five"),
       7 => println!("seven"),
       _ => (),
   }

Concise Control Flow with if let

rust 复制代码
let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
rust 复制代码
if let Some(3) = some_u8_value {
    println!("three");
}

Choosing between match and if let depends on what you're doing in your particular situation and whether gaining conciseness is an appropriate trade-off for losing exhaustive checking.

In other words, you can think of if let as syntax sugar for a match that runs code when the value matches one pattern and then ignores all other values.

rust 复制代码
let mut count = 0;
match coin {
    Coin::Quarter(state) => println!("State quarter from {:?}!", state),
    _ => count += 1,
}

If you have a situation in which your program has logic that is too verbose to express using a match, remember that if let is in your Rust toolbox as well.

rust 复制代码
let mut count = 0;
if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}!", state);
} else {
    count += 1;
}
相关推荐
難釋懷几秒前
JavaScript基础-移动端常见特效
开发语言·前端·javascript
海姐软件测试10 分钟前
Postman参数化设置如何设置?
开发语言·jmeter
松树戈12 分钟前
Java常用异步方式总结
java·开发语言
weisian15112 分钟前
Java常用工具算法-3--加密算法2--非对称加密算法(RSA常用,ECC,DSA)
java·开发语言·算法
Uncertainty!!14 分钟前
python函数装饰器
开发语言·python·装饰器
x-cmd14 分钟前
[250331] Paozhu 发布 1.9.0:C++ Web 框架,比肩脚本语言 | DeaDBeeF 播放器发布 1.10.0
android·linux·开发语言·c++·web·音乐播放器·脚本语言
Asthenia041222 分钟前
从迷宫到公式:为 NFA 构造正规式
后端
Asthenia041238 分钟前
像整理玩具一样:DFA 化简和状态等价性
后端
myloveasuka40 分钟前
[Linux]从硬件到软件理解操作系统
linux·开发语言·c++
bst@微胖子1 小时前
Flutter项目之登录注册功能实现
开发语言·javascript·flutter