Rust 学习笔记 (五)枚举和模式匹配

#ProgrammingLanguage/Rust

enum IpAddrKind {
	V4,
	V6, 
}

let four = IpAddrKind::V4; //注意这里使用的是 ;;
let six = IpAddrKind::V6;

fn route(ip_type: IpAddrKind) { }

route(IpAddrKind::V4);
route(IpAddrKind::V6);

例子:

enum IpAddrKind {
    V4,
    V6,
}

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

我们可以使用以下方法更简洁地表示相同的概念 通过将数据直接放入每个枚举变量中来实现枚举,而不是结构内部的枚举。

enum IpAddr {
       V4(String),
       V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1")); 
let loopback = IpAddr::V6(String::from("::1"));

这是枚举对比结构体的另一个优势,每一个变量可以有不同类型和数量的关联数据。

实际使用中,可以在枚举中放入任何类型的变量。甚至放入另一个枚举。

枚举中也可以定义method:

impl Message {
		fn call(&self){
			//method body would be defined here
		}
}

let m = Message::Write(String::from("hello"));
m.call();

Option

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

Option枚举非常有用,甚至包含在prelude中,不用明确的放在范围内。 可以直接使用Some 和 None,不用Option::前缀。

let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;

--------------------------------------------------------------
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is not satisfied

在rust中 i8 和 Option,不是一个类型,i8有表示的变量,是明确可用的,但是Option是一个枚举,可能表示None。

match表达式是一个控制流构造,与enum一起使用时会执行此操作:它将运行不同的代码,具体取决于它具有enum的哪个变体,并且该代码可以使用匹配值内的数据。

the match control flow operator rutt 中math 非常强大,允许比较一串模式的变量,并执行匹配变量的对应的代码。

match采用第一匹配机制,遇到第一个匹配的就跳转执行相应代码。

与每个分支关联的代码是一个表达式,而在匹配分支中该表达式的结果值是为整个匹配表达式返回的值。

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

patterns that bind to values

fn value_in_cents(coin: Coin) -> u32 { match coin {
		Coin::Penny => 1, 
		Coin::Nickel => 5, 
		Coin::Dime => 10, 
		Coin::Quarter(state) => {
			println!("State quarter from {:?}!", state);
			25 
		},
	} 
}
--------------------------------------------------------------
call value_in_cents(Coin::Quarter(UsState::Alaska))

**matching with option**

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

上面的代码,i 和Some包含的变量绑定。

match 和 枚举 组合使用,在rust中很常见: 匹配枚举、绑定内部数据和变量、执行关联代码。

match are exhaustive(精准匹配)

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

--------------------------------------------------------------
error[E0004]: non-exhaustive patterns: `None` not covered

占位符:省略写法

let some_u8_value = 0u8; 
match some_u8_value {
	1 => println!("one"),
	3 => println!("three"), 
	5 => println!("five"), 
	7 => println!("seven"), 
	_ => (), //必须放在最后,匹配往后的所有可能
}

然后,如果我们只匹配一种情况,match的写法会稍显啰嗦,对于这种情况,rust官方推荐使用 if let 代码结构。

concise control flow with if let (使用if let简洁的控制流程) ⚠️if let语法使您可以将if和let组合成一种较为冗长的方式来处理与一种模式匹配的值,而忽略其余模式。

let some_u8_value = Some(0u8); 
match some_u8_value {
	Some(3) => println!("three"),
	_ => (), 
}
if let Some(3) = some_u8_value { 
	println!("three");
}

笔记补充 Enums allow you to define a type by enumerating its possible variants 枚举允许使用者定一个类型,什么类型呢,列举其可能的变体的类型. ⚠️枚举是一个自定义的数据类型。

以ip地址举例,任何ip只能是v4和v6其中一个。

enum IpAddrKind {
    V4,
    V6,
}

IpAddrKind现在是一种自定义数据类型,我们可以在代码的其他地方使用它。

构建枚举类型实例:

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

请注意,枚举变量在其标识符下命名空间内,我们使用双冒号进行表示。

fn route(ip_kind: IpAddrKind) {}

route(IpAddrKind::V4);
route(IpAddrKind::V6);
``````rust

定义和数据类型关联的枚举
``` rust
enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));

使用枚举而不是结构还有另一个优势:每个变体可以具有不同类型和数量的关联数据。

enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));
``````rust

``` rust
#![allow(unused_variables)]
fn main() {
    struct Ipv4Addr {
        // --snip--
    }

    struct Ipv6Addr {
        // --snip--
    }

    enum IpAddr {
        V4(Ipv4Addr),
        V6(Ipv6Addr),
    }
}

此代码说明可以将任何类型的数据放入枚举变量内:字符串,数字类型或结构

enum Message {
    Quit,//Quit has no data associated with it at all.
    Move { x: i32, y: i32 },//Move includes an anonymous struct inside it.	
    Write(String),//Write includes a single String.
    ChangeColor(i32, i32, i32),//ChangeColor includes three i32 values.
}

我们甚至可以在枚举中定义method

impl Message {
        fn call(&self) {
            // method body would be defined here
        }
    }

    let m = Message::Write(String::from("hello"));
    m.call();

枚举Option关联范式类型,常常用于,表示一个值,这个值可能是某个类型的值也可能是一个None的情况。

enum Option<T> {
    Some(T), //泛式数据类型
    None,
}

使用Optiion时:它将运行不同的代码,具体取决于它具有哪种枚举变体,并且该代码可以使用match来控制。

match允许你将一个值和众多模式进行比较,并且根据和模式的匹配执行不同的代码。 模式可以由文字值,变量名,通配符和许多其他内容组成

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

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

match arm : 一个arm有两个部分, 一个是pattern(模式)和对应这个模式的一段代码. 第一个匹配不到,匹配第二个,以此类推。

模式绑定到值

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

_占位符的简写

fn main() {
    let some_u8_value = 0u8;
    match some_u8_value {
        1 => println!("one"),
        3 => println!("three"),
        5 => println!("five"),
        7 => println!("seven"),
        _ => (),
    }
}

_表示的pattern,表示任何未明确指出可能的值,类似C语言中switch的default.

但是,在我们只关心其中一种模式匹配的情况下,match表达式可能有点啰嗦 ,这时候就需要使用 if let。

匹配Option枚举的值,只有当值是3的时候,执行匹配成功的代码打印字符串“three”。

let some_u8_value = Some(0u8);
match some_u8_value {
	Some(3) => println!("three"),
	_ => (),
}
``````rust

可以简写为:
``` rust
if let Some(3) = some_u8_value {
        println!("three");
    }

However, you lose the exhaustive checking that match enforces. 将失去对匹配进行强制执行的详尽检查。