zoukankan      html  css  js  c++  java
  • 八.枚举与模式匹配

    定义枚举

    enum IpAddrKind {
        V4,
        V6,
    }
    

    枚举值

    枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。

    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
    fn route(ip_type: IpAddrKind) { }
    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
    
    enum IpAddrKind {
        V4,
        V6,
    }
    struct IpAddr {
        kind: IpAddrKind,
        address: String,
    }
    let home = IpAddr {
        kind: IpAddrKind::V4,
        address: String::from("127.0.0.1"),
    };
    let loopback = IpAddr {
        kind: IpAddrKind::V6,
        address: String::from("::1"),
    };
    

    直接将数据附加到枚举的每个成员上

    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));
    
    struct Ipv4Addr {
    // --snip--
    }
    struct Ipv6Addr {
    // --snip--
    }
    enum IpAddr {
        V4(Ipv4Addr),
        V6(Ipv6Addr),
    }
    
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }
    
    • Quit 没有关联任何数据。
    • Move 包含一个匿名结构体。
    • Write 包含单独一个 String 。
    • ChangeColor 包含三个 i32 。
    struct QuitMessage; // 类单元结构体
    struct MoveMessage {
        x: i32,
        y: i32,
    }
    struct WriteMessage(String); // 元组结构体
    struct ChangeColorMessage(i32, i32, i32); // 元组结构体
    

    这里回顾下相关概念

    元组:let tup: (i32, f64, u8) = (500, 6.4, 1);

    类单元结构体:struct QuitMessage;

    元组结构体:struct Color(i32, i32, i32);

    元组只有类型没有名称,且都是以()定义,而结构体都是以{}定义,除了元组结构体。

    C++的枚举只支持值类型的,Rust的枚举应该是支持了更多类型。

    # enum Message {
    #     Quit,
    #     Move { x: i32, y: i32 },
    #     Write(String),
    #     ChangeColor(i32, i32, i32),
    # }
    #
    impl Message {
        fn call(&self) {
            // 在这里定义方法体
        }
    }
    let m = Message::Write(String::from("hello"));
    m.call();
    

    这里的write也没看见实现,反正只是返回一个枚举对象,暂时不管。

    前面结构体已经用到过self,等同于c++的this理解,默认的unmut等同于C++的成员函数后面加const,且一般传递&self等同于传递的是this对象本身的引用,只传self因为所有权变更只在特定情况下才能使用。

    Option 枚举和其相对于空值的优势

    Option 是标准库定义的另一个枚举。 Option 类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值。

    Rust 并没有很多其他语言中有的空值功能。

    空值概念上讲是一个因为某种原因目前无效或缺失的值。由于空值引起的程序问题并非空值本身存在问题,而在于具体的实现。

    Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 Option

    enum Option<T> {
        Some(T),
        None,
    }
    

    Some(T) 和 None 仍是 Option 的成员。

    目前需要知道的就是 意味着 Option 枚举的 Some 成员可以包含任意类型的数据

    let some_number = Some(5);
    let some_string = Some("a string");
    let absent_number: Option<i32> = None;
    

    如果使用 None 而不是 Some ,需要告诉 Rust Option 是什么类型的,因为编译器只通
    过 None 值无法推断出 Some 成员保存的值的类型。

    当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。那么, Option 为什么就比空值要好呢?

    简而言之,因为 Option 和 T (这里 T 可以是任何类型)是不同的类型,编译器不允
    许像一个肯定有效的值那样使用 Option

    let x: i8 = 5;
    let y: Option<i8> = Some(5);
    let sum = x + y;//错误,类型不匹配
    
    ^ no implementation for `i8 + std::option::Option<i8>`
    

    错误信息意味着 Rust 不知道该如何将 Option 与 i8 相加,因为它们的类型不同。

    当在 Rust 中拥有一个像 i8 这样类型的值时,编译器确保它总是有一个有效的值。我们可以自信使用而无需做空值检查。只有当使用 Option (或者任何用到的类型)的时候需要担心可能没有值,而编译器会确保我们在使用值之前处理了为空的情况

    为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 Option 中。

    Rust默认定义的变量如果为空则直接编译报错,这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。

    当有一个 Option 的值时,如何从 Some 成员中取出 T 的值来使用它,后面再看。

    match 控制流运算符

    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,
        }
    }
    

    以上语义上基本上是类似C++形式的枚举switch。

    => 运算符将模式和将要运行的代码分开,每一个分支之间使用逗号分隔。如果分支代码较短的话通常不使用大括号。

    绑定值的模式

    匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。这也就是如何从枚举成员中
    提取值的。

    #[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
            },
        }
    }
    value_in_cents(Coin::Quarter(UsState::Alaska))
    

    value_in_cents(Coin::Quarter(UsState::Alaska)) , Coin::Quarter(UsState::Alaska) 作为形参coin的实参。当将值与每个分支相比较时,匹配到Coin::Quarter(state) 。这时, state 绑定的将会是值 UsState::Alaska 。

    匹配 Option和匹配 Some(T)

    Option是类型,Some(T)是成员

    fn plus_one(x: Option<i32>) -> Option<i32> {
        match x {
            None => None,
            Some(i) => Some(i + 1),
        }
    }
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
    

    匹配是穷尽的

    fn plus_one(x: Option<i32>) -> Option<i32> {
        match x {
             Some(i) => Some(i + 1),//错误^ pattern `None` not covered
        }
    }
    

    Rust 中的匹配是 穷尽的(exhaustive):必须穷举到最后的可能性来使代码有效。

    _ 通配符

    Rust 也提供了一个模式用于不想列举出所有可能值的场景

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

    _ 模式会匹配所有的值。通过将其放置于其他分支之后, _ 将会匹配所有之前没有指定的
    可能的值。 () 就是 unit 值,所以 _ 的情况什么也不会发生。

    match 在只关心 一个 情况的场景中可能就有点啰嗦了。为此 Rust 提供了 if let

    if let 简单控制流

    处理只匹配一个模式的值而忽略其他模式的情况。

    let some_u8_value = Some(0u8);
    if let Some(3) = some_u8_value {
    	println!("three");
    }
    
    let some_u8_value = Some(0u8);
    match some_u8_value {
        Some(3) => println!("three"),
        _ => (),
    }
    
    let coin = Coin::Penny;
    let mut count = 0;
    if let Coin::Quarter(state) = coin {
        println!("State quarter from {:?}!", state);
    } 
    else {
        count += 1;
    }
    

    Coin::Quarter(state)为枚举成员,coin为实际要比较的实参。

  • 相关阅读:
    串口通信中接收数据时延迟处理与缓存处理的解决方案(C#)
    串口通讯接收数据的处理
    在C#程序设计中使用Win32类库
    C# Mutex对象学习经验
    我眼中的C# 3.0 Written by Allen Lee
    利用C#鼠标拖动TreeView节点
    richtextbox内文字自动滚动的例子
    在十六进制字符串与数值类型之间转换 C# 编程指南
    如何:指定符号位置和加载行为
    杂记20110321
  • 原文地址:https://www.cnblogs.com/kuikuitage/p/13424187.html
Copyright © 2011-2022 走看看