7.枚举和模式匹配

一、enum枚举

1.1 定义枚举类型和对应的数据

//定义枚举
#[derive(Debug)]
enum IpAddrKind{
    IPv4,
    IPv6,
}

struct Ipaddr{
    kind: IpAddrKind,  //设置kind为IpAddrKind的枚举类型
    address: String,
}

fn route(ip_addr: &Ipaddr){
    println!("ip_type = {:#?}", ip_addr.kind);
}

fn main(){
    let home = Ipaddr{
        kind: IpAddrKind::IPv4,   //使用枚举
        address: String::from("127.0.0.1"),
    };

    let loopback = Ipaddr{
        kind: IpAddrKind::IPv6,
        address: String::from("::1"),
    };

    route(&loopback);
    route(&home);
}

上述将枚举与结构体进行绑定,显得过于麻烦,可以按照下面的简写

enum IpAddr{
    IPv4(String),
    IPv6(String),
}

这样简写就可以

  • 不需要额外使用struct
  • 每个数据可以拥有不同的类型以及关联的数据量
enum IpAddr{
    IPv4(String),
    IPv6(String),
    V4(u8,u8,u8,u8),
}

fn main(){
    let home = IpAddr::IPv4(String::from("192.168.0.1"));
    let loopback = IpAddr::IPv6(String::from("::1"));
    let v4 = IpAddr::V4(192, 168, 0, 5);
}

1.2 再来一个比较复杂的

#[derive(Debug)]
enum Message {
    Quit,                       //没有关联任何数据
    Move { x: i32, y: i32 },    //包含一个匿名结构体
    Write(String),              //包含单独一个 String
    ChangeColor(i32, i32, i32), //包含3个i32
}

impl Message {
    fn call(&self) {
        println!("self = {:#?}", self);  //输出self = Write(
                                            //    "hello",
                                             //)
    }
}

fn main() {
    let g = Message::Quit;
    let m = Message::Move { x: 12, y: 24 };
    let w = Message::Write(String::from("Hello"));
    let c = Message::ChangeColor(0, 255, 255);

    w.call();   //w就是call的self值
}

1.3 枚举空间计算

  • Message::Quit不需要任何空间;
  • Message::Move需要两个i32值类型的空间,以此类推;
  • 枚举值需要的最大空间是存储其最大成员所需要的空间大小;

二、Option枚举

2.1 概念

  • Option编码了一个非常普遍的场景,即有值和没值
  • 这就意味着编译器需要检查是否处理了所有应该处理的情况;
  • Rust并没有空值(NULL)的概念;
  • Rust拥有一个可以编码存在或不存在概念的枚举----Option<T>,T是一个泛指类型;
enum Option<T> {
    Some(T),
    None,
}
  • Option<T> 不需要将其显式引入作用域,也不需要Option:: 前缀来直接使用 Some 和 None;
  • 如果使用 None 而不是 Some,需要告诉RustOption<T>是什么类型的;

2.2 举例

1. Some和None

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

2. option<T>和T

  • 这里的T是可以是任何类型;
  • option<T>T是不同的类型;

下面的代码会报错

fn main() {
    let some_number = Some(5);  //编译器自动推断为i32类型
    let som_string = Some("A String");   //编译器自动推断为字符切片(&str)类型
    let absent_number:Option<i32> = None;  //写None时必须显示的声明

    //Option<T>和T是不同的类型, 不可以把Option<T>直接当成T
    //若想使用Option<T>中的T,必须将它转换为T
    let x: i8 = 5;
    let y: Option<i8> = Some(5);

    let sum = x + y;  //^ no implementation for `i8 + Option<i8>`
}

报错信息如下
在这里插入图片描述

  • 在对 Option<T> 进行 T 的运算之前必须将其转换为 T;
  • 能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况,提高代码的安全性;

三、match模式匹配

3.1 基本用法

  • match匹配允许将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码;
  • 模式可由字面量、变量、通配符和许多其他内容构成;
enum Coin{
    Penny,
    Nickel,
    Dime,
    Quarter,
}

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

fn main(){
    value_in_cents(Coin::Penny);
}

3.2 绑定值模式

  • 与在枚举成员中提取值相同,匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值;
  • 下面这个例子可以修改一个枚举的成员用来存放数据,打开最终的UsState的值
#[derive(Debug)]
enum UsState{
    Alabama,
    Alaska,
}

enum Coin{
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),  //Quarter关联一个数据
}

fn value_in_cents(coin: Coin) -> u8{
    match coin{
        Coin::Penny => {
            println!("Penny");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => { //将state绑定到Quarter所存的值上
            println!("State quarter from {:?}!", state);
            25
        }
    }
}

fn main(){
    let c = Coin::Quarter(UsState::Alaska); 
    println!("{}", value_in_cents(c))  //State quarter from Alaska!
}

四、匹配Option<T>

  • 之前使用Option<T>时,是为了从 Some 中取出其内部的 T 值;
  • 这里像处理Coin枚举那样使用match 处理Option<T>中的成员;

下面的函数获取一个Option<i32>,如果其中含有一个值,将其加1;如果其中没有值,函数应该返回 None。

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

    println!("five = {:#?}, six = {:#?} none = {:#?}", five, six, none)
}

打印结果

five = Some(
    5,
), six = Some(
    6,
) none = None

五、穷举

match匹配需要穷举,如下的代码match必须列举完0~255。

fn main() {
    let x:u8 = 9;

    match x {
        0 => {
            println!("0");
        }

        1 => {
            println!("1");
        }
    }
}

代码报错如下,意思就是没有覆盖2到最大值。在这里插入图片描述
这问题加一个_通配符就能解决

fn main() {
    let x:u8 = 9;

    match x {
        0 => {
            println!("0");
        }

        1 => {
            println!("1");
        }

        _ => {
            println!("other");
        }
    }
}

六、if let

  • 只关心一种匹配而忽略其它匹配的情况可以使用if let;
  • 这样也就放弃了穷举的可能;
fn main(){
    let v = Some(3u8);

    match v {
        Some(3) => println!("three"),
        _ => (),
    }

    //和上面的match效果相同
    if let Some(3) = v {
        println!("three");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贱贱的剑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值