[rust]枚举、模式匹配、Option

116 阅读6分钟

枚举的定义

枚举可以是不同的类型

enum ChatKind {
   OneToOne, // 等同于空结构体,或者叫类单元结构体
   GroupChat { conversation_name: String, owner_id: i64 }, // 等同于自定义结构体
   P2PChat(i64, i64), // 等同于元组结构体
   TopicChat(String), // 等同于元组结构体
}

方法实现与模式匹配

  1. 枚举类型常与match语句一起使用
  2. -是一个通配符,表示无任何匹配的情况
impl ChatKind {
    fn print(&self) {
        match self {
            ChatKind::OneToOne => {
                println!("这个一个私聊")
            }
             ChatKind::GroupChat{conversation_name, owner_id} => {
                 println!("群名={}, 群主ID={}", conversation_name, owner_id)
             }
            ChatKind::P2PChat(from, to) => {
                println!("{}发给{}的p2p消息", from, to)
            }
            ChatKind::TopicChat(topic) => {
                println!("{}, 该话题正聊得火爆", topic)
            }
            _ => { println!("没有匹配到的枚举类型") }
        }
    }
}

创建枚举与方法调用

fn main() {
    let single = ChatKind::OneToOne;
    single.print();

    let group = ChatKind::GroupChat {
        conversation_name: "Rust学习群".to_string(),
        owner_id: 112233,
    };
    group.print();

    let p2p = ChatKind::P2PChat(112233, 445566);
    p2p.print();

    let topic = ChatKind::TopicChat("户外爬山推荐".to_string());
    topic.print()
}

Option官方枚举

Option是标准库定义的一个枚举

#[derive(Copy, Eq, Debug, Hash)]
#[rustc_diagnostic_item = "Option"]
#[lang = "Option"]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is manually implemented equivalently
pub enum Option<T> {
    /// No value.
    #[lang = "None"]
    #[stable(feature = "rust1", since = "1.0.0")]
    None,
    /// Some value of type `T`.
    #[lang = "Some"]
    #[stable(feature = "rust1", since = "1.0.0")]
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}

使用方式

  1. 要使用枚举里携带的值就要用match匹配
  2. 赋值为None必须为参数申明Option类型和泛型类型
  3. match可以和let一起使用
fn main() {
    let one = Some(1);
    let zero: Option<i32> = None;
    let two = 2;
    let one_val = match one {
        None => { 0 }
        Some(val) => { val }
    };
    let three = one_val + two;
    println!("相加={}", three)
}

4.match可以和if let一起使用

n main() {
    if let Some(val) = plus_add(Some(1), Some(2)){
        println!("返回值不为空 = {}",val)
    }else {
        println!("返回值为空")
    }
}

fn plus_add(x: Option<i32>, y: Option<i32>) -> Option<i32> {
    let x_val = match x {
        None => { 0 }
        Some(val) => { val }
    };

    let y_val = match y {
        None => { 0 }
        Some(val) => { val }
    };

    if x_val + y_val == 0 {
        return None;
    }
    return Some(x_val + y_val);
}

unwarp

unwrap 方法非常直接,当调用它时,它会尝试拆包 OptionResult,返回其中包含的值。如果拆包失败(即 OptionNoneResultErr),程序会立即崩溃并抛出一个 panic。

fn main() {
    let some_value = Some(10);
    let none_value: Option<i32> = None;

    let value = some_value.unwrap();
    println!("{}", value); // 输出: 10

    // 下面这行代码会导致程序 panic,因为 `none_value` 是 `None`
    // let value = none_value.unwrap();
}

因为 unwrap 可能导致程序崩溃,通常建议使用更安全的方法来处理 Option,例如:

  • unwrap_or:在 OptionNone 时提供一个默认值。

  • unwrap_or_else:在 OptionNone 时执行一个闭包。

  • expect:类似于 unwrap,但可以提供自定义的 panic 信息。

  • match模式匹配:使用 match 或者 if let 来处理 Option

fn main() {
    let some_value = Some(10);
    let none_value: Option<i32> = None;

    // 使用 unwrap_or 提供默认值
    let value = some_value.unwrap_or(0);
    println!("{}", value); // 输出: 10

    let value = none_value.unwrap_or(0);
    println!("{}", value); // 输出: 0

    // 使用 expect 提供自定义 panic 信息
    let value = some_value.expect("Should have a value");
    println!("{}", value); // 输出: 10

    // 下面这行代码会导致程序 panic,并输出 Should have a value
    /*let value = none_value.expect("Should have a value");
    println!("{}", value);*/

    // 模式匹配
    match some_value {
        Some(val) => println!("Value is: {}", val),
        None => println!("Value is missing"),
    }
}

and_then

定义

impl<T> Option<T> {
    pub fn and_then<U, F>(self, f: F) -> Option<U>
    where
        F: FnOnce(T) -> Option<U>,
    {
        // method implementation
    }
}

and_then 接受一个闭包作为参数,该闭包对 Option 包含的值进行操作并返回一个新的 Option。如果原 OptionNone,则直接返回 None,否则返回闭包的结果。可以方便地处理包含 Option 类型逻辑的链式操作。

fn step1(x: i32) -> Option<i32> { // 接收一个大于0的数并加1,否则返回None
    if x > 0 {
        Some(x + 1)
    } else {
        None
    }
}

fn step2(x: i32) -> Option<i32> { // 接收一个偶数并除以2,否则返回None
    if x % 2 == 0 {
        Some(x / 2)
    } else {
        None
    }
}

fn main() {
    let result = Some(4)
        .and_then(step1);
    println!("{:?}", result); // 输出: Some(5)

    let result = Some(4)
        .and_then(step1)
        .and_then(step2);

    println!("{:?}", result); // 输出: None
}

map

定义

impl<T> Option<T> {
    pub fn map<U, F>(self, f: F) -> Option<U>
    where
        F: FnOnce(T) -> U,
    {
        // method implementation
    }
}

如果 OptionSome(T) 类型,则应用提供的闭包将 T 转换为一个新的值 U 并返回 Some(U)。如果 OptionNone,则直接返回 None


#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let some_option = Some(2);
    let result = some_option.map(|x| x + 3);
    println!("{:?}", result); // 输出: Some(5)

    let none_option: Option<i32> = None;
    let result = none_option.map(|x| x + 3);
    println!("{:?}", result); // 输出: None

    let user_option = Some(User { name: "Alice".to_string(), age: 30 });
    let name_option = user_option.map(|user| user.name);
    println!("{:?}", name_option); // 输出: Some("Alice")
}

is_some_and

impl<T> Option<T> {
    pub fn is_some_and<F>(&self, f: F) -> bool
    where
        F: FnOnce(&T) -> bool,
    {
        // method implementation
    }
}

用于检查 Option 是否包含某个值并且这个值满足指定的条件。这个方法可用于简化需要同时检查值的存在性和条件的代码逻辑,使代码更加简洁和易读。

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}
fn main() {
    let some_option = Some(4);

    let result = some_option.is_some_and(|x| x > 2);
    println!("{}", result); // 输出: true

    let result = some_option.is_some_and(|x| x < 2);
    println!("{}", result); // 输出: false


    let none_option: Option<i32> = None;
    let result = none_option.is_some_and(|x| x > 2);
    println!("{}", result); // 输出: false

    let user_option = Some(User { name: "Alice".to_string(), age: 30 });
    // 检查 user_option 是否包含一个年龄大于20的用户
    let is_age_greater_than_20 = user_option.is_some_and(|user| user.age > 20);
    println!("{}", is_age_greater_than_20); // 输出: true

    let user_option = Some(User { name: "Alice".to_string(), age: 30 });
    // 检查 user_option 是否包含一个名字是 "Bob" 的用户
    let is_name_bob = user_option.is_some_and(|user| user.name == "Bob");
    println!("{}", is_name_bob); // 输出: false
}

结合?运算符

Option 类型和 ? 运算符(又称作 “question mark”)可以结合使用以简化错误处理和短路返回机制。? 运算符不仅可以用于 Result 类型,同样也可以用于 Option 类型。

例如当一个函数返回 Option 类型并想要在某个点快速返回 None,则可以使用? 运算符避免繁琐的匹配模式,并使代码更加简洁和可读。

如果 Option 类型?不为None则返回实际值。

fn find_and_double(input: Option<&str>) -> Option<i32> {
    let input_str = input?; // 如果input为None,则直接返回None
    let number = extract_number(input_str)?; // // 如果函数返回值为None,则直接返回None
    Some(number * 2)
}

fn extract_number(input: &str) -> Option<i32> {
    input.trim().parse::<i32>().ok()
}

fn main() {
    let valid_input = Some("42");
    let invalid_input = Some("not a number");
    let none_input: Option<&str> = None;

    println!("{:?}", find_and_double(valid_input));  // 输出: Some(84)
    println!("{:?}", find_and_double(invalid_input));  // 输出: None
    println!("{:?}", find_and_double(none_input));  // 输出: None
}

ok_or_else

允许将一个 Option 转换为 Result,并在 OptionNone 的时候提供一个默认的错误值(由一个闭包或函数生成)。

fn main() {
    // 一个 Option 中包含了一个值
    let some_value: Option<i32> = Some(10);
    let result: Result<i32, String> = some_value.ok_or_else(|| "This is an error".to_string());
    match result {
        Ok(val) => println!("Got the value: {}", val), // Got the value: 10
        Err(err) => println!("Error: {}", err),
    }

    // 一个 Option 中不包含任何值
    let none_value: Option<i32> = None;
    let result: Result<i32, String> = none_value.ok_or_else(|| "This is an error".to_string());
    match result {
        Ok(val) => println!("Got the value: {}", val),
        Err(err) => println!("Error: {}", err), // Error: This is an error
    }
}

as_ref