Rust的枚举类型和模式匹配

枚举的定义

创建枚举类型

enum NUM
{
    one,
    two,
}

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

fn main()
{
    let a=NUM::one;
    let b=NUM::two;
}

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

 #[derive(Debug)]
 enum NUM{
     one,
     two,
 }
fn main()
{
    let a=NUM::one;
    let b=NUM::two;
    println!("{:?}{:}",a,b);
}

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

enum与struct混合使用

struct _type{
    num:NUM,
    a:u32,
    b:String,
 }

我们在一个结构体的字段中创建了一个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"),
    };
}

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

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

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

enum类型绑定数据类型

枚举的独特之处:

enum Ex1{
    Name(String),
    Age(u32),
}

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

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

fn main()
{
    let n1=Ex1::Name(String::from("ylh"));
    let n2=Ex1::Age(18);

    println!("{:#?}{:#?}",n1,n2);
}

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


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

enum Ex1{
    Name(String,String),
    Age(u32,u32,u32),
}

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

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

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

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

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

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

struct A;
struct B{
	x:i32
	y:i32
}
struct C(String);
struct D(i32,i32,i32);

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

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

impl Message{
	fn show(&self){
		//定义一些方法	
	}
}
fn main()
{	
	let a=Message::Write(String::from("ylh));
	a.show();	//调用此方法
}

Option枚举

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

//Option在标准库中的定义:
enum Option<T>{
    None,
    Some(T),
}

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

  • Option一个特殊的枚举,我们可以直接使用None和Some,不需要Option<T>::限定符。
  • 是一个泛型,可以接受并自动推断任意在标准库中的类型,基本上就是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;
}

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

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

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

简而言之:如果你有一个值想要是空,你就把他放在Option<T>里面,这里面的值就表明了,我的值有可能为空,可能在现在,可能在以后。
所以我们不能将确定的类型与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));
}

我们有一个函数,接受一个枚举,用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
        },
    }
}

在这里插入图片描述

枚举绑定值的情况

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

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

运行得:
在这里插入图片描述
我们在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,
    }
}

在这里插入图片描述

占位符

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

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

}

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

  • 出现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),
        _ => (),
    }
}

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

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

fn main() {
    let config_max = Some('e');
    if let Some(i)=config_max{
        println!("Yes: {}",i);
    }
}

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

当然我们也可以使用else:

fn main() {
    let config_max:std::option::Option<i32> = None;
    if let Some(i)=config_max{
        println!("Yes: {}",i);
    }else{
        println!("None!");
    }
}

在这里插入图片描述

总结

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

posted @ 2022-09-19 12:54  hugeYlh  阅读(43)  评论(0编辑  收藏  举报  来源