一、枚举的定义

通过在代码中定义一个IpAddrKind枚举来表现IP地址中的IPv4和IPv6。这被称为枚举的成员(variants):

enum IpAddrKind {
	v4,
	v6,
}

1、枚举值

fn main() {
    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"),
    };
}

枚举的成员位于其标识符的明明空间中,并使用两个冒号分开。这样设计的益处是现在IpAddrKind::V4IpAddrKind::V6都是IpAddrKind类型的。
这里我们定义了一个有两个字段的结构体 IpAddr:IpAddrKind(之前定义的枚举)类型的 kind 字段和 String 类型 address 字段。我们有这个结构体的两个实例。第一个home,它的 kind 的值是 IpAddrKind::V4 与之相关联的地址数据是 127.0.0.1。第二个实例loopbackkind 的值是 IpAddrKind 的另一个成员,V6,关联的地址是 ::1
我将使用另一种更简洁的方式来表达,仅仅使用枚举并将数据直接放进每个枚举成员而不是将枚举作为结构体的一部分。

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"));
}

我们可通IpAddr标准库进行IP地址的枚举操作。IpAddr in std::net – Rust (rust-lang.org)
虽然标准库中包含了一个IpAddr的定义,仍然可以创建和使用我们自己定义而不会有冲突,因为我们并没有将标准库中的定义引入作用域。

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

Option是标准库定义的另一个枚举。Option类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值。Rust没有很多其他语言中有的空值功能。空值(Null)是一个值,它代表没有值。在有空值的语言中,变量重视这两种状态之一:空值和非空值。
Rust并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是Option<T>,而且它定义于标准库中。

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

Option<T>枚举已经被包含在prelude中,不需要再将其显式引入作用域。另外,它的成员也不需要Option::前缀来直接使用SomeNone
<T>语法是Rust功能,它是一个泛类型参数。目前你所需指导<T>意味着Option枚举的Some成员可以包含任意类型的数据,同时每一个用于T位置的具体类型使得Option<T>整体作为不同的类型。

fn main() {
    let some_number = Some(5);
    let some_char = Some('e');

    let absent_number: Option<i32> = None;
}

some_number 的类型是 Option<i32>some_char 的类型是 Option<char>,这(与 some_number)是一个不同的类型。因为我们在 Some 成员中指定了值,Rust 可以推断其类型。对于 absent_number, Rust 需要我们指定 Option 整体的类型,因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型。这里我们告诉 Rust 希望 absent_number 是 Option<i32> 类型的。
![[Pasted image 20221121202613.png]]
当有一个 Some 值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。那么,Option<T> 为什么就比空值要好呢?
因为 Option<T> 和 T(这里 T 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 Option<T>

二、控制流运算符match

Rust有一个叫做match的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据乡匹配的模式执行响应代码。match的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(conin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

fn main {}

函数value_in_cents中的match块,看上去与if表达式十分相似,但是有一个很大的区别:在if表达式中需要返回一个布尔值,而这里的表达式则可以返回任何类型。
match分支由模式和它所关联的代码组成。第一个分支采用了值Coin::Penny作为模式,并紧跟着一个=>运算符用于将模式和代码区分开来。不同分支直接使用逗号分隔。当这个match表达式执行时,它会将产生的结果值依次与每个分支中的模式相比较。如果匹配模式成功,则与该模式相关联的代码就会被继续执行;而假如模式匹配失败,则会继续执行下一个分支。

1、绑定值的模式

匹配分支另一个有趣的地方在于它们可以绑定被匹配对象的部分值。

#[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));
}

我们在代码中调用value_in_cents(Coin::Quarter(UsState::Alaska))Coin::Quarter(UsState::Alaska)就会作为coin的值传入函数。这个值会一次与每个分支进行匹配,一直到Coin::Quarter(state)模式才会终止匹配。这时,值UsState::Alaska就会被绑定到state上。接着,我们就可以println!表达式中使用这个绑定了。

2、匹配Option<T>

fn main() {
    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);
}

let six = plus_one(five)在调用plus_one(five)时,plus_one函数体中的变量x被绑定为值Some(5)。因为Some(5)没有办法匹配上None,语句会继续向下匹配,之后会匹配到Some(i)
let none = plus_one(None)在运行时x变成None,一次进入match表达式 ,并匹配到None分支。

3、匹配必须穷举所有的可能

match需要我们必须穷尽所有可能性,来确保代码是合法有效的。如果没有穷尽所有可能,这就会导致出现报错。

4、通配符(_

fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => reroll(),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
    fn reroll() {}
}

这里的_模式可以匹配任何值。通过将它放置于其他分支之后,可以使其帮我们匹配所有没有被现实指定出来的可能的形式。与它对应的代码块里只有一个()空元组,所以在_匹配下什么都不会发生。

三、简单控制流if let

if let能让我们通过一种不那么烦琐的语法结合使用iflet,并处理那些只用关心某一匹配而忽略其他匹配的情况。
它匹配一个 config_max 变量中的 Option<u8> 值并只希望当值为 Some 成员时执行代码:

fn main() {
    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("The maximum is configured to be {}", max),
        _ => (),
    }
}

为了满足 match 表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 _ => (),这样增加了代码可读性,因此使用if let更为简单。

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {}", max);
    }
}

if let语法使用一对以=隔开的模式与表达式。它的工作方式与 match 相同,表达式对应match中的输入,而模式则对应第一个分支。
match 和 if let 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。

原文地址:http://www.cnblogs.com/ColoFly/p/16914286.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性