从零开始学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;
}
相关推荐
西瓜本瓜@1 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
UFIT1 小时前
Python函数与模块笔记
开发语言·python
机智的人猿泰山1 小时前
java kafka
java·开发语言·kafka
Y1nhl2 小时前
搜广推校招面经八十一
开发语言·人工智能·pytorch·深度学习·机器学习·推荐算法·搜索算法
Algorithm15762 小时前
谈谈接口和抽象类有什么区别?
java·开发语言
YiSLWLL2 小时前
使用Tauri 2.3.1+Leptos 0.7.8开发桌面小程序汇总
python·rust·sqlite·matplotlib·visual studio code
yu4106212 小时前
Rust 语言使用场景分析
开发语言·后端·rust
良艺呐^O^2 小时前
uniapp实现app自动更新
开发语言·javascript·uni-app
细心的莽夫2 小时前
SpringCloud 微服务复习笔记
java·spring boot·笔记·后端·spring·spring cloud·微服务
jack_xu4 小时前
高频面试题:如何保证数据库和es数据一致性
后端·mysql·elasticsearch