Rust权威指南之枚举和模式匹配

434 阅读5分钟

一. 枚举

我们在Java和C等语言中也使用过枚举,现在让我们看一下Rust的枚举,这里我们使用Rust的枚举定义IP地址分类:IPV4/IPV6

enum IpAddrKind {
    V4,
    V6
}

下面看一下如何使用枚举值:

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

fn main() {
    let ipv4 = IpAddrKind::V4;
    let ipv6 = IpAddrKind::V6;
    println!("{:?} - {:?}", ipv4, ipv6); // V4 - V6
}

接下看一下如何函数参数中使用枚举值:

fn route(ip_type: IpAddrKind) {}
// 使用
route(IpAddrKind::V4);
route(IpAddrKind::V6);

现在IP枚举类型可以表示但是无法存储IP地址数据,这里我们可以使用结构体配合一起使用:

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

#[derive(Debug)]
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") };
}

其实Rust枚举是允许将其关联的数据嵌入枚举变体内,下面看例子:

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

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

其实Rust枚举可以嵌套各种各样的数据类型,下面举个例子:

#[derive(Debug)]
struct Data {}

#[derive(Debug)]
enum Message {
    Quit, // 空结构体
    Move { x: i32, y: i32 }, // 匿名结构体
    Write(String), // 元组结构体
    Color(i32, i32, i32), // 元组结构体
    Data, // 结构体
}

其实这些数据类型都是看作是一个个结构体。

二. Option

Option枚举是一个非常常见且实用的枚举。Option类型描述了一种值可能不存在的情况。不存在就会出现空值的情况,空值可能会触发程序错误。Rust中没有空置,但是我们可以使用Option,先看一下标准库中的定义:

pub enum Option<T> {
    /// No value.
    None,
    /// Some value of type `T`.
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}

Option在标准库中存在例子的,下面看一下例子:

let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;

下面列出一些常用方法,参考来源:Rust - Option 常用方法

方法解释方法解释
is_some/is_none若是Some则返回true/若是None则返回trueinspect若是Some(v)则调用 f(&v),随后返回Self
is_some_andSome并且f函数返回trueok_orOption<T>变为Resuilt<T,E>,若是None则返回Err(err)
unwrap若是Some(v),则返回v,如果None,则直接panicok_or_elseok_or一样,无非是err值变为err()的返回值
expectunwrap一样,只不过None后可以带 msg数据as_derefOption<T>转为Deref解引用后的DOption<D>
unwrap_or若是Some(v),则返回v,如果None,则返回defaultas_deref_mutas_deref一样,不同的是 是DerefMut解引用后的值
unwrap_or_else若是Some(v),则返回v,如果None,则返回f函数提供的返回值andselfoptb若都是Some,则返回optb,否则返回None
unwrap_or_default若是Some(v),则返回v,如果None,则返回T::default函数的返回值and_thenand一样 , 不同的是optbf()函数的返回值
mapOption<T>类型 通过f函数转为 Option<U>类型filter若是None,则直接返回None,若是Some(v),则判断predicate(v)的返回值;若predicate返回ture,则返回Some(v),否则返回None
map_ormap一样,不同的是 若是 None则返回default;所以 map_or返回的不是 Option<U>而是U,因为它一定存在返回值orselfoptb都是None,则返回Noneselfoptb其中一个是Some,则返回是Some的那一个;selfoptb都是Some,优先返回self
map_or_elseor_elseor一样 , 不同的是 optbf()函数的返回值
xorselfoptb其中一个是Some,则返回是Some的那一个,selfoptb一样,则返回Noneget_or_insert_withget_or_insert一样,不同的是插入值是f()函数的返回值
insert插入一个新值,旧值被删除;注意返回值是插入后的可变借用,而非是旧值take取出值,在其位置留下 None
get_or_insertselfNone,则可以插入值,并返回插入后的可变借用;若selfSome(v),则无法插入,并返回v的可变借用replace插入新值,返回旧值
containsSome(v)中的v==x, 则返回truezipselfother都是Some,则返回 Option<(T,U)>,其他情况则都返回None
zip_withselfother都是Some,则返回 Some(f(T,U)),也就是返回f()函数的返回值;其他情况则都返回 NoneflattenOption<Option<T>> 转换为 Option<T>

二. match

Rust中有一个异常强大的控制流程运算符:match,它允许将一个值与一系列的模式相比较,并根据匹配的模式执行相应代码。模式可由字面量、变量名、通配符和许多其他东西组成。

这里可以将match看成一个增强版的switch!

下面看一个例子:

enum Week {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

fn week_to_str(week: Week) -> u32 {
    match week {
        Week::Monday => 1,
        Week::Tuesday => 2,
        Week::Wednesday => 3,
        Week::Thursday => 4,
        Week::Friday => 5,
        Week::Saturday => 6,
        Week::Sunday => 7,
    }
}

其实很简单类似switch的写法,下面在展示几个写法, 直接上例子:

// 判断是否是工作日
fn is_work_day(week: Week) -> bool {
    match week {
        Week::Saturday | Week::Sunday => false, // 如果是周六和周日 不是工作日返回false
        _ => true, // 其他可以不做判断直接返回true
    }
}

接着我们在看一个Option和match之间的配合,直接看例子:

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        Some(data) => Some(data + 1),
        _ => None
    }
}
// 这里可以使用我们上面的Option的map方法进行简化写法。
fn plus_one(x: Option<i32>) -> Option<i32> {
    x.map(|data| data + 1) // map函数给我们做了上面的步骤
}

三. if let

if let能让我们通过一种不那么繁琐的语法结合使用if和let,并处理那些只关心某一种匹配而忽略其他匹配的情况。

let  some_value = Some(9);
match some_value {
  	Some(9) => println!("Ok"),
  	_ => (),
}

上面的例子中,其实我们只关心当some_value的值时9的时候,但是上面的代码写的就比较啰嗦,下面我们使用if let优化下:

if let Some(data) = some_value {
  	println!("Ok")
};

if let可以看成是match的语法糖。它只在值满足某一特定的情况时运行代码,而忽略其他所有的可能性。

更多的用法可以看:通过例子学习Rust的match

这部分内容在18章的时候会详细介绍,这里先有一个大概的了解,下一章见!