• Rust的枚举类型和模式匹配


    枚举的定义

    创建枚举类型

    enum NUM
    {
        one,
        two,
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们创建了一个枚举类型,此枚举包含两个枚举变量,one和two
    我们可以给变量赋予枚举值:

    fn main()
    {
        let a=NUM::one;
        let b=NUM::two;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我么能否使用println!来尝试出枚举的值?
    很遗憾,不能,不过,我们可以使用上一节我们学到的:

     #[derive(Debug)]
     enum NUM{
         one,
         two,
     }
    fn main()
    {
        let a=NUM::one;
        let b=NUM::two;
        println!("{:?}{:}",a,b);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意加上#[derive(Debug)]这个说明:这样我们就可以打印出枚举的值了。
    在这里插入图片描述

    enum与struct混合使用

    struct _type{
        num:NUM,
        a:u32,
        b:String,
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们在一个结构体的字段中创建了一个num字段,其是我们刚才创建的enum NUM类型。

    现在,我们可以创建结构体的实例变量

    fn main()
    {
        let sb1=_type{
            num:NUM::one,
            a:88,
            b:String::from("ylh"),
        };
        let sb2=_type{
            num:NUM::two,
            a:100,
            b:String::from("abcd"),
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们来访问这个结构体实例变量

    println!("{:#?}{:#?}",sb1,sb2);
    
    • 1

    别忘了在结构体前面加上 #[derive(Debug)] 标识符。
    在这里插入图片描述

    enum类型绑定数据类型

    枚举的独特之处:

    enum Ex1{
        Name(String),
        Age(u32),
    }
    
    • 1
    • 2
    • 3
    • 4

    我们创建了一个枚举,这个枚举的字段绑定了两个值,分别是String的Name和u32的Age,
    我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。

    我们像上面使用结构体那样,使用这个枚举,创建一个实例变量

    fn main()
    {
        let n1=Ex1::Name(String::from("ylh"));
        let n2=Ex1::Age(18);
    
        println!("{:#?}{:#?}",n1,n2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    别忘了加上控制打印的说明,运行如下:
    在这里插入图片描述


    此外,我们还可以在枚举中定义含有多个属性的字段

    enum Ex1{
        Name(String,String),
        Age(u32,u32,u32),
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是结构体没有的,而枚举特有的一个特性。

    甚至我们还可以有内嵌了多种多样的类型的枚举:

    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这个枚举有四个含有不同类型的成员:

    • Quit 没有关联任何数据。
    • Move 类似结构体包含命名字段。
    • Write 包含单独一个 String。
    • ChangeColor 包含三个 i32。

    这使得我们的枚举更加具有灵活性
    但是,如果我们要用结构体来描述这个枚举,他很可能是这样的:

    struct A;
    struct B{
    	x:i32
    	y:i32
    }
    struct C(String);
    struct D(i32,i32,i32);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们创建了四个结构体来描述这一个枚举,可以清晰地看到结构体处理不同的类型数据太麻烦了。
    因为枚举是单独一个类型,使用这个类型可以轻易的处理多种不同的类型。

    枚举也可以像结构体那样定义方法

    impl Message{
    	fn show(&self){
    		//定义一些方法	
    	}
    }
    fn main()
    {	
    	let a=Message::Write(String::from("ylh));
    	a.show();	//调用此方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Option枚举

    Rust没有像C那样的NULL类型,NULL(空值)指的是:空值尝试表达的概念仍然是有意义的:空值是一个因为某种原因目前无效或缺失的值。
    Rust提供了一个枚举Option让我们来处理空值的情况:

    //Option在标准库中的定义:
    enum Option<T>{
        None,
        Some(T),
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这是一个拥有存在和不存在的枚举:Option

    • Option一个特殊的枚举,我们可以直接使用None和Some,不需要Option::限定符。
    • 是一个泛型,可以接受并自动推断任意在标准库中的类型,基本上就是C++的泛型模板template。

    Option 枚举是如此有用以至于它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 Option:: 前缀来直接使用 Some 和 None。即便如此 Option 也仍是常规的枚举,Some(T) 和 None 仍是 Option 的成员。

    我们可以指定任意类型,T会自动推断。

    但我们指定是一个None时,Rust 需要我们指定 Option 整体的类型,因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型

    注意:我们无须自己写一个Option,此枚举是标准库自带的,可以直接使用
    最后一行我们告诉 Rust 希望 absent_number 是 Option 类型的:

    fn main(){
    	let some_number = Some(5);
    	let some_char = Some('e');
        
    	let absent_number:Option<i32> = None;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Rust不知道如何将确定的类型与Option类型的值相加

    let a:i32=8;
    let b=Some(16);
    let sum=a+b;		//出错		
    
    • 1
    • 2
    • 3

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

    简而言之:如果你有一个值想要是空,你就把他放在Option里面,这里面的值就表明了,我的值有可能为空,可能在现在,可能在以后。
    所以我们不能将确定的类型与Option 类型在一起做任何操作,Rust也提供了一些方法.
    你可以把Option类型转换为T类型,这样他就一定不会是一个空值了,可以与确定的类型在一起操作了。
    那么如何转换呢,我还没有学到,以后再说。

    match控制流结构

    match 表达式是这么一个处理枚举的控制流结构:它会根据枚举的成员运行不同的代码,这些代码可以使用匹配到的值中的数据。

    enum Ex2{
        a,
        b,
        c,
        d
    }
    
    fn cmp(this:Ex2)->u32{
        match this {
            Ex2::a=>1,
            Ex2::b=>5,
            Ex2::c=>10,
            Ex2::d=>100,
        }
    }
    
    fn main()
    {
        let num=Ex2::c;
        println!("{}",cmp(num));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们有一个函数,接受一个枚举,用match来匹配枚举,当具有不同的枚举值时,他们的值也不同。
    在这里插入图片描述
    match语句返回一个表达式,我们使用{}大括号将几条语句或小表达式集合在一起成为一个大的表达式:

    fn cmp(this:Ex2)->u32{
        match this {
            Ex2::a=>{
                println!("a");
                1
            },
            Ex2::b=>{
                println!("b");
                5
            }
            Ex2::c=>{
                println!("c");
                10
            }
            Ex2::d=>{
                println!("d");
                100
            },
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    枚举绑定值的情况

    在一个枚举中包含另一个枚举值

    #[derive(Debug)]
    enum No{
        One,
        Two,
        Three,
    }
    enum Ex2{
        a,
        b,
        c,
        d(No)
    }
    
    fn cmp(this:Ex2)->u32{
        match this {
            Ex2::a=>1,
            Ex2::b=>5,
            Ex2::c=>10,
            Ex2::d(value)=>{
                println!("{:?}",value);
                100
            },
        }
    }
    fn main()
    {
        let num=Ex2::d(No::Three);
        println!("{}",cmp(num));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    运行得:
    在这里插入图片描述
    我们在num变量中的d里面绑定了 No::Three,当进入到match时,会匹配,发现d是符合条件的,进入println!宏,打印此value,即No::Three。

    匹配Option枚举

    fn main(){
        let num1=Some(55);
        let name_none:Option<i32>=None;
    
        test_match(num1);
        test_match(name_none);
    
        println!("{:?}",num1);
        println!("{:?}",name_none);
    
    }
    
    fn test_match(this:Option<i32>)->Option<i32>{
        match this {
            Some(i)=>{
                println!("Find it !!!");
                Some(i+10)
            }
            None=>None,
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    占位符

    枚举必须是穷尽的,他必须包含所有可能出现的情况。

    fn main() {
        fn plus_one(x: i32) -> i32 {
            match x {
               5=>{
                println!("yes!");
                return 10;
                }
                _=>{
                    println!("None!");
                    return 100;
                }
            }
        }
        let five = 5;
        let none = plus_one(50);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    _表示任意情况,把它放在最后面,可以保证枚举的结束。

    • 出现5:则打印yes,返回10
    • 出现50:则匹配 _ 打印None 返回100.

    if let 简洁控制流

    使用match控制流:
    我们创建一个的Option枚举变量,只希望为Some时打印消息:

    fn main() {
        let config_max = Some('e');
        let _num:std::option::Option<i32>=None;
        match config_max {    
            Some(max) => println!("The maximum is configured to be {}", max),
            _ => (),
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    所有不是Some和_的都会直接忽略。

    现在我们使用 if let 来重新实现这一实例:

    fn main() {
        let config_max = Some('e');
        if let Some(i)=config_max{
            println!("Yes: {}",i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以更加简洁的表示这个实例,就像match的占位符一样,不符合条件的,直接会被省略,这样我们就少写了好多代码。

    当然我们也可以使用else:

    fn main() {
        let config_max:std::option::Option<i32> = None;
        if let Some(i)=config_max{
            println!("Yes: {}",i);
        }else{
            println!("None!");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    总结

    定义与使用枚举,枚举的绑定与对比结构体的优势,match匹配枚举,match匹配Option枚举,占位符,if let控制流。

  • 相关阅读:
    机器学习案例(六):加密货币价格预测
    LoadRunner调用Analysis组件的相关技术点及知识总结
    深入Docker实战(第2版):构建、部署和优化容器化应用
    mybatisplus代码生成覆盖
    dp入门课(一)
    Qt浏览器模块的几点说明
    顺序表与链表(上)
    虹科分享 | 测试与验证复杂的FPGA设计(2)——如何在IP核中执行面向全局的仿真
    如何用 Spring Security 集成 CAS 认证 (二)
    人工智能-机器学习-深度学习-概述
  • 原文地址:https://blog.csdn.net/jj6666djdbbd/article/details/126931574