• Rust权威指南之枚举和模式匹配


    一. 枚举

    我们在Java和C等语言中也使用过枚举,现在让我们看一下Rust的枚举,这里我们使用Rust的枚举定义IP地址分类:IPV4/IPV6

    enum IpAddrKind {
        V4,
        V6
    }
    
    • 1
    • 2
    • 3
    • 4

    下面看一下如何使用枚举值:

    #[derive(Debug)]
    enum IpAddrKind {
        V4,
        V6
    }
    
    fn main() {
        let ipv4 = IpAddrKind::V4;
        let ipv6 = IpAddrKind::V6;
        println!("{:?} - {:?}", ipv4, ipv6); // V4 - V6
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    接下看一下如何函数参数中使用枚举值:

    fn route(ip_type: IpAddrKind) {}
    // 使用
    route(IpAddrKind::V4);
    route(IpAddrKind::V6);
    
    • 1
    • 2
    • 3
    • 4

    现在IP枚举类型可以表示但是无法存储IP地址数据,这里我们可以使用结构体配合一起使用:

    #[derive(Debug)]
    enum IpAddrKind {
        V4,
        V6
    }
    
    #[derive(Debug)]
    struct IpAddr {
        kind: IpAddrKind,
        address: String,
    }
    
    fn main() {
        let home = IpAddr { kind: IpAddrKind::V4, address: String::from("127.0.0.1") };
        let loopback = IpAddr { kind: IpAddrKind::V6, address: String::from("::1") };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    其实Rust枚举是允许将其关联的数据嵌入枚举变体内,下面看例子:

    #[derive(Debug)]
    enum IpAddr {
        V4(String),
        V6(String),
    }
    
    fn main() {
        let home = IpAddr::V4(String::from("127.0.0.1"));
        let loopback = IpAddr::V6(String::from("::1"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其实Rust枚举可以嵌套各种各样的数据类型,下面举个例子:

    #[derive(Debug)]
    struct Data {}
    
    #[derive(Debug)]
    enum Message {
        Quit, // 空结构体
        Move { x: i32, y: i32 }, // 匿名结构体
        Write(String), // 元组结构体
        Color(i32, i32, i32), // 元组结构体
        Data, // 结构体
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其实这些数据类型都是看作是一个个结构体。

    二. Option

    Option枚举是一个非常常见且实用的枚举。Option类型描述了一种值可能不存在的情况。不存在就会出现空值的情况,空值可能会触发程序错误。Rust中没有空置,但是我们可以使用Option,先看一下标准库中的定义:

    pub enum Option<T> {
        /// No value.
        None,
        /// Some value of type `T`.
        Some(#[stable(feature = "rust1", since = "1.0.0")] T),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Option在标准库中存在例子的,下面看一下例子:

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

    下面列出一些常用方法,参考来源:Rust - Option 常用方法

    方法解释方法解释
    is_some/is_none若是Some则返回true/若是None则返回trueinspect若是Some(v)则调用 f(&v),随后返回Self
    is_some_andSome并且f函数返回trueok_orOption变为Resuilt,若是None则返回Err(err)
    unwrap若是Some(v),则返回v,如果None,则直接panicok_or_elseok_or一样,无非是err值变为err()的返回值
    expectunwrap一样,只不过None后可以带 msg数据as_derefOption转为Deref解引用后的DOption
    unwrap_or若是Some(v),则返回v,如果None,则返回defaultas_deref_mutas_deref一样,不同的是 是DerefMut解引用后的值
    unwrap_or_else若是Some(v),则返回v,如果None,则返回f函数提供的返回值andselfoptb若都是Some,则返回optb,否则返回None
    unwrap_or_default若是Some(v),则返回v,如果None,则返回T::default函数的返回值and_thenand一样 , 不同的是optbf()函数的返回值
    mapOption类型 通过f函数转为 Option类型filter若是None,则直接返回None,若是Some(v),则判断predicate(v)的返回值;若predicate返回ture,则返回Some(v),否则返回None
    map_ormap一样,不同的是 若是 None则返回default;所以 map_or返回的不是 Option而是U,因为它一定存在返回值orselfoptb都是None,则返回Noneselfoptb其中一个是Some,则返回是Some的那一个;selfoptb都是Some,优先返回self
    map_or_elseor_elseor一样 , 不同的是 optbf()函数的返回值
    xorselfoptb其中一个是Some,则返回是Some的那一个,selfoptb一样,则返回Noneget_or_insert_withget_or_insert一样,不同的是插入值是f()函数的返回值
    insert插入一个新值,旧值被删除;注意返回值是插入后的可变借用,而非是旧值take取出值,在其位置留下 None
    get_or_insertselfNone,则可以插入值,并返回插入后的可变借用;若selfSome(v),则无法插入,并返回v的可变借用replace插入新值,返回旧值
    containsSome(v)中的v==x, 则返回truezipselfother都是Some,则返回 Option<(T,U)>,其他情况则都返回None
    zip_withselfother都是Some,则返回 Some(f(T,U)),也就是返回f()函数的返回值;其他情况则都返回 NoneflattenOption> 转换为 Option

    二. match

    Rust中有一个异常强大的控制流程运算符:match,它允许将一个值与一系列的模式相比较,并根据匹配的模式执行相应代码。模式可由字面量、变量名、通配符和许多其他东西组成。

    这里可以将match看成一个增强版的switch!

    下面看一个例子:

    enum Week {
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday
    }
    
    fn week_to_str(week: Week) -> u32 {
        match week {
            Week::Monday => 1,
            Week::Tuesday => 2,
            Week::Wednesday => 3,
            Week::Thursday => 4,
            Week::Friday => 5,
            Week::Saturday => 6,
            Week::Sunday => 7,
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    其实很简单类似switch的写法,下面在展示几个写法, 直接上例子:

    // 判断是否是工作日
    fn is_work_day(week: Week) -> bool {
        match week {
            Week::Saturday | Week::Sunday => false, // 如果是周六和周日 不是工作日返回false
            _ => true, // 其他可以不做判断直接返回true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接着我们在看一个Option和match之间的配合,直接看例子:

    fn plus_one(x: Option<i32>) -> Option<i32> {
        match x {
            Some(data) => Some(data + 1),
            _ => None
        }
    }
    // 这里可以使用我们上面的Option的map方法进行简化写法。
    fn plus_one(x: Option<i32>) -> Option<i32> {
        x.map(|data| data + 1) // map函数给我们做了上面的步骤
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三. if let

    if let能让我们通过一种不那么繁琐的语法结合使用if和let,并处理那些只关心某一种匹配而忽略其他匹配的情况。

    let  some_value = Some(9);
    match some_value {
      	Some(9) => println!("Ok"),
      	_ => (),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面的例子中,其实我们只关心当some_value的值时9的时候,但是上面的代码写的就比较啰嗦,下面我们使用if let优化下:

    if let Some(data) = some_value {
      	println!("Ok")
    };
    
    • 1
    • 2
    • 3

    if let可以看成是match的语法糖。它只在值满足某一特定的情况时运行代码,而忽略其他所有的可能性。

    更多的用法可以看:通过例子学习Rust的match

    这部分内容在18章的时候会详细介绍,这里先有一个大概的了解,下一章见!

  • 相关阅读:
    vue3+vite+ts 组件中自动导入 ref 和 reactive
    VoLTE端到端业务详解 | 网元标识类
    Vue集成Echarts
    C语言找出一个二维数组中的鞍点,即该位置上的元素在该行上最大,在该列上最小,也可能没有鞍点
    (JavaEE)线程的状态
    跨界技术:SOCKS5代理在电商、爬虫与游戏领域的应用
    世界前沿技术发展报告2023《世界航天技术发展报告》(四)载人航天技术
    【java计算机毕设】博客管理系统 javaweb springboot vue mysql
    Android进阶——Handler底层fd监听之epoll机制
    计算机毕业设计hadoop+spark+hive知识图谱酒店推荐系统 酒店数据分析可视化大屏 酒店爬虫 高德地图API 酒店预测系统 大数据毕业设计
  • 原文地址:https://blog.csdn.net/yhflyl/article/details/127928714