Rust的枚举与模式匹配

我爱海鲸 2024-01-28 17:12:31 rust学习

简介enum、match

1、定义枚举

   枚举允许我们列举所有可能的值来定义一个类型

如:   IP地址:lPv4、lPv6

enum IpAddressKind{
    V4,
    V6
}

定义枚举的时候命名使用CamelCase的命名风格

枚举值

fn main() {
    let four = IpAddressKind::V4;

    let six = IpAddressKind::V6;

    route(four);

    route(six);

    route(IpAddressKind::V4);
}

#[derive(Debug)]
enum IpAddressKind{
    V4,
    V6
}

fn route(ip_kind:IpAddressKind) {
    println!("{:?}",ip_kind);
}

将数据附加到枚举的变体中

fn main() {
    let four = IpAddressKind::V4;

    let six = IpAddressKind::V6;

    let s1 = IpAddr{
        kind:four,
        address:String::from("127.0.0.1")
    };
    
    let s2 = IpAddr{
        kind:six,
        address:String::from("::1")
    };
}

#[derive(Debug)]
enum IpAddressKind{
    V4,
    V6
}

struct IpAddr {
    kind:IpAddressKind,
    address:String
}

上面的代码中我们可以把枚举作为结构体的值,但是枚举的数据可以直接添加到枚举中

enum lpAddr {
    v4(String),
    v6(String),
}

优点:

   不需要额外使用struct

   每个变体可以拥有不同的类型以及关联的数据量

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

我们来看一下例子:

fn main() {
    let four = IpAddr::V4(127, 0, 0, 1);

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

    println!("{:?}",four);
    println!("{:?}",six);

}


#[derive(Debug)]
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String)
}

标准库中的lpAddr

struct lpv4Addr {}
struct lpv6Addr {}
enum lpAddr {
    v4(Ipv4Addr),
    v6(Ipv6Addr),
}

我们再来看一下其他例子:

fn main() {
    let q = Message::Quit;
    let m = Message::Move { x: 12, y: 13 };
    let w = Message::Write(String::from("455"));
    let c = Message::ChangeColor(127, 255, 0, );
}

#[derive(Debug)]
enum Message {
    Quit,
    Move{x:i32,y:i32},
    Write(String),
    ChangeColor(i32,i32,i32)
}

为枚举定义方法

使用impl这个关键字来定义方法

fn main() {
    let q = Message::Quit;
    let m = Message::Move { x: 12, y: 13 };
    let w = Message::Write(String::from("455"));
    let c = Message::ChangeColor(127, 255, 0, );

    m.call()
}

#[derive(Debug)]
enum Message {
    Quit,
    Move{x:i32,y:i32},
    Write(String),
    ChangeColor(i32,i32,i32)
}

impl Message {
    fn call(&self) {
        
    }
}

2、Option枚举

定义于标准库中

在Prelude(预导入模块)中

描述了:某个值可能存在(某种类型)或不存在的情况

Rust没有Null。其它语言中:

Null是一个值,它表示“没有值”

一个变量可以处于两种状态:空值(null)、非空

Null引用:Billion Dollar Mistake

Null的问题在于:当你尝试像使用非 Null值那样使用Null值的时候,就会引起某种错误

Null的概念还是有用的:因某种原因而变为无效或缺失的值

Rust中类似 Null概念的枚举- Option<T>

标准库中的定义:

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

它包含在 Prelude(预导入模块)中。可直接使用:

  • Option<T>
  •  Some(T)
  •  None

例如:

fn main() {
    let some_number = Some(5);
    let some_string = Some("A String");

    let absent_number: Option<i32> = None;
}

Option<T>比Null好在哪?

Option<T>和T是不同的类型,不可以把Option<T>直接当成T(例子)

fn main() {
    let x:i32 = 5;
    let y:Option<i32> = Some(5);

    let sum = x + y;
}

上述代码中就会报错,因为i32类型和Option<i32>类型是不一样的,无法进行相加

若想使用Option<T>中的T,必须将它转换为T

而在C#中:

  • string a = null;
  • - string b = a +“12345";
fn main() {
    let x:i32 = 5;
    let y:Option<i32> = Some(5);

    let sum = x + y.unwrap_or_default();

    println!("{}",sum)
}

3、控制流运算符– match

允许一个值与一系列模式进行匹配,并执行匹配的模式对应的代码

模式可以是字面值、变量名、通配符...

fn main() {
    let x:i32 = 5;
    let y:Option<i32> = Some(5);

    let sum = x + y.unwrap_or_default();

    println!("{}",sum)
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter
}

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

绑定值的模式

匹配的分支可以绑定到被匹配对象的部分值。

   因此,可以从enum变体中提取值

fn main() {
    let c = Coin::Quarter(UsState::Alaska);

    println!("{}",value_in_cents(c));
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState)
}
#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska
}

fn value_in_cents(coin:Coin)->i8 {
    match coin {
        Coin::Penny => {
            println!("penny");
            1
        },
        Coin::Nickel=> 5,
        Coin::Dime=> 15,
        Coin::Quarter(state)=> {
            println!("State quarter from {:?}!",state);
            25
        },
    }
}

匹配Option<T>

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
}

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

match匹配必须穷举所有的可能

_通配符:替代其余没列出的值

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

if let

它是一个简单的控制流表达式

处理只关心一种匹配而忽略其它匹配的情况

fn main() {
    let v = Some(3);
    match v {
        Some(3) => println!("three"),
        _ => (),
    }
    
    if let Some(3) = v {
        println!("three");
    }
}

更少的代码,更少的缩进,更少的模板代码。

放弃了穷举的可能

可以把if let看作是match的语法糖

搭配else

fn main() {
    let v = Some(2);
    match v {
        Some(3) => println!("three"),
        _ => (),
    }
    
    if let Some(3) = v {
        println!("three");
    } else {
        println!("other");
    }
}

你好:我的2025