10.枚举、模式匹配

枚举的定义

  • ipV4和ipV6使用IpAddrKind来进行枚举
fn main() {
enum IpAddrKind {
    V4,
    V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
}
//定义函数可以获取IpAddrKind
fn route(ip_type: IppAddrKind)
{
	
}
route(IpAddrKind::V4);
route(IpAddrKind::V6);
  • 目前没有实际存储IP地址数据的方法,只知道它的类型,下面代码:
fn main() {
enum IpAddrKind {
    V4,
    V6,
}

struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};

let loopback = IpAddr {
    kind: IpAddrKind::V6,
    address: String::from("::1"),
};
}
  • 这段可以改变,将枚举数据放入枚举成员而不是放入结构体。
fn main() {
enum IpAddr {
    V4(String),
    V6(String),
}

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

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

  • 同时,这样做还有一个好处,每个成员可以处理不同类型和数量的数据。
fn main() {
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

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

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

  • 同时,标准库也定义了IppAddr,它是将数据放入结构体中,
fn main() {
struct Ipv4Addr {
    // --snip--
}

struct Ipv6Addr {
    // --snip--
}

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

  • 而标准是将成员中的数据放入到不同的结构体中,然后将结构体放入枚举中。
  • 可以将任意类型的数据放入枚举成员中:例如字符串、数字类型或者结构体。甚至可以包含另一个枚举!
fn main() {
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
}

  • Quit 没有关联任何数据。
  • Move 包含一个匿名结构体。
  • Write 包含单独一个 String。
  • ChangeColor 包含三个 i32。
fn main() {
struct QuitMessage; // 类单元结构体
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
}

  • 同时可以使用不同的结构体存储上述变量。
  • impl关键字也可以用来定义枚举。
fn main() {
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

impl Message {
    fn call(&self) {
        // 在这里定义方法体
    }
}

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

  • 这里使用&self使用self中的值。

Option枚举

rust中不存在空值,但是存在可以编码存在或者不存在概念的枚举,是Option,定义在标准库之下

fn main() {
enum Option<T> {
    Some(T),
    None,
}
}
  • option被包含在prelude之中,不需要显式引出,它的成员也是这样,可以直接使用someNone
  • 是一个未讲到的语法,是一个泛型类型参数,代表着someNone可以包含任何类型的数据。

#![allow(unused)]
fn main() {
let some_number = Some(5);
let some_string = Some("a string");

let absent_number: Option<i32> = None;
}

  • 如果使用 None 而不是 Some,需要告诉 Rust Option<T> 是什么类型的,因为编译器只通过 None 值无法推断出 Some 成员保存的值的类型。
  • 当有一个 Some 值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。
let x: i8 = 5;
let y: Option<i8> = Some(5);

let sum = x + y;

  • 这段代码是无法运行的,因为尝试将option和i8相加。
  • RUST只有在使用Option时需要担心是否有值。
  • Option<T>运算之前需要先将其转换为T类型。
  • 紧接着的match表达式就是这样一个处理枚举的控制流结构。

match控制流运算符

  • if、loop等相同,match也是控制流。
  • match将一个值和一系列模式比较,匹配出相应的模式执行相应代码。有点像是case函数。
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和if最大的区别是if返回一个bool值,而match可以是任何类型的。
  • match会从上到下,执行分支,可以拥有多个分支。如果要在分支中运行多行代码,需要使用大括号。
match coin
	{
		Coin::Penny =>{
		println!("Lucky penny!");
		1
		}
		Coin::Nickel => 5,
		Coin::Dime => 10,
		Coin::Quarter => 25,
}

绑定值match控制流的模式

  • 在25美分上有彩蛋,为州的名字。
#[derive(Debug)] // 这样可以立刻看到州的名称
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {}

  • 之后对state中对应州值的函数代码:
#[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));
}

在这里插入图片描述

  • 这里要注意的是,不需要再对UsState进行重新创建变量,而是直接使用了state这个关键词。

匹配Option

  • 之前是使用some取出内部的T,同时也可以用match处理Option<T>!。
  • 编写函数获取Option,如果有值,值加一。如果没有值,返回None。
fn main() {
    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);
}

匹配Some(T)

  • 接下来是一段官方文档
    在这里插入图片描述

Rust中匹配的有穷性

  • rust中匹配必须是有穷尽的,需要将所有的可能结果都写进去。
    ~官方文档:
    在这里插入图片描述

通配模式和_占位符

  • 一个例子:对3和7采取操作,对其他行为采取默认操作。
fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        other => move_player(other),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
    fn move_player(num_spaces: u8) {}
}

  • 命名一个other变量,它的分支代码通过其传递给move_player函数。
  • 即使没有列出u8所有值,也能正常编译,同时,最后一个模式将匹配所有未被列出的特殊值,为通配模式。
  • 同时,也可以使用’_'来匹配任意值,但不绑定该值,这个符号代表不想使用通配模式获取的值。
fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => reroll(),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
    fn reroll() {}
}

  • 可以在最后一个分支使用:
fn main() {
    let dice_roll = 9;
    match dice_roll {
        3 => add_fancy_hat(),
        7 => remove_fancy_hat(),
        _ => (),
    }

    fn add_fancy_hat() {}
    fn remove_fancy_hat() {}
}

if let 简单控制流

  • if let可以用来处理只匹配一个模式的值,忽略其他模式的情况。
fn main() {
let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
}

  • 在使用if let之后,代码变成了:
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
    println!("three");
}
}

  • if letmatch的一个语法糖,同时其中可以添加一个else,这个else对用着match中的_
    接着是一个官方文档:
    在这里插入图片描述
  • 巧妙利用matchif let会事半功倍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值