Rust 中的枚举和控制流运算

2022-06-26 16:51:29   最后更新: 2022-06-26 16:51:29   访问数量:90




 

枚举类型对于 Java 程序员来说不会陌生,它是列举常量成员的非常好用的工具。在 rust 中也同样如此,并且在 rust 中,枚举类型比其他语言中更为常用,尤其是 Option、Result 等语言自身定义的枚举类型,为 rust 本身添加了非常强大而独特的语法特性。

 

 

 

与 java 语言枚举中关注枚举的类型和值不同,rust 中的枚举专注于类型,枚举成员本身是不对应具体的值的。

 

2.1 枚举类型的定义

 

例如,下面的枚举类型定义了 IPv4 和 IPv6 两个成员:

 

enum IpAddrKind { V4, V6, } fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6; route(IpAddrKind::V4); route(IpAddrKind::V6); } fn route(ip_kind: IpAddrKind) {}

 

 

2.2 枚举类型的数据

 

上面的例子中,定义了一个枚举类型,并且创建了相应类型的的变量。

 

但我们往往不仅希望变量体现出具体的类型,还希望变量具备具体的值。

 

此时我们可以将枚举类型与具体的值再进行一层封装,从而得到一个同时包含类型和值的结构体。

 

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

 

 

进一步,我们可以通过 rust 的语法糖来简化上述结构体的定义:

 

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

 

 

 

Option 是 rust 标准库定义的一个枚举。Option 的存在,尝试去解决令无数软件开发工程师抓狂的空指针、空引用问题。

 

3.1 Option 的定义

 

Option 的定义如下:

 

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

 

 

看起来非常简单,如果一个函数或是表达式要返回一个 T 类型的数据,并且 T 类型的数据有可能为空。那么用 Option 封装将会是一个更好的选择。因为 T 类型的数据如果为空值,而使用者忽略了这一情况,将会产生程序的 panic,这通常是我们不希望看到的。

 

3.2 Option 变量的创建

 

可以这样获得一个 Option 的数据:

 

let x: Option<u32> = Some(2); assert_eq!(x.is_some(), true); let x: Option<u32> = None; assert_eq!(x.is_some(), false); let x: Option<u32> = None; assert_eq!(x.is_none(), true);

 

 

一旦一个返回值被 Option 封装起来,使用者就必须处理值为 None 的情况,这一显式的处理,避免了意外的空引用、空指针的产生。

 

3.3 Option 数据的获取

 

要想获取 Option 中的实际数据,可以通过很多种方法,此处介绍 unwrap 方法。

 

1. unwrap 方法

 

unwarp 方法需要保证有值,一旦值为 None,那么就会让程序 panic。

 

// 如果值为 None,则 unwrap 将导致 panic let x = Some("air"); assert_eq!(x.unwrap(), "air");

 

 

2. unwrap_or 方法

 

与 unwrap 方法不同,unwrap_or 方法允许传递一个参数,当值为 None 时,则以这个参数作为返回值。

 

assert_eq!(Some("car").unwrap_or("bike"), "car"); assert_eq!(None.unwrap_or("bike"), "bike");

 

 

3. unwrap\_or\_else 方法

 

unwrap\_or 方法让我们能够提供默认的返回值,但更为高级的是 unwrap\_or_else 方法,它允许我们创建一个函数或表达式来决定当值为 None 时返回什么。

 

let vec = vec![1,2,3]; let a: &i32 = vec.get(4).unwrap_or_else(||{ vec.get(0) });

 

 

 

正如在 java 语言中,枚举类型搭配 switch-case 可以让代码显得十分简洁易于维护。在 Rust 中,通过强大的控制流运算符 match 搭配枚举也同样可以实现类似的效果。

 

4.1 match 的使用

 

下面就是一个 match 控制流的示例:

 

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

 

 

4.2 match 获取枚举值

 

对于某个枚举拥有他的枚举数据值时,也可以进行 match:

 

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 } } }

 

 

4.3 Option 与 match 匹配

 

有了上述 4.2 节的示例,我们就可以来处理 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);

 

 

这个示例定义了一个函数,它获取一个 Option<i32> ,如果其中含有一个值,将其加一。如果其中没有值,函数应该返回 None 值,而不尝试执行任何操作。

 

4.4 通配模式

 

需要注意的是,如果要使用 match 控制流,枚举类型包含的每一个类型都必须要出现在 match 块中。

 

但有时,我们希望有一种模式可以用来代替所有其他情况,类似于其他语言 switch 语句中的 default 关键字,在 Rust 中,同样是支持这一特性的,那就是 _ 占位符:

 

let dice_roll = 9; match dice_roll { 3 => add_fancy_hat(), 7 => remove_fancy_hat(), _ => (), } fn add_fancy_hat() {} fn remove_fancy_hat() {}

 

 

 

很多时候,我们只想简洁地获取一个枚举对应的值或表达式,通过 match 表达式往往会显得太过复杂。此时,if let 可能会是更好的选择,例如:

 

#[derive(Debug)] enum UsState { Alabama, Alaska, // --snip-- } enum Coin { Penny, Nickel, Dime, Quarter(UsState), } fn main() { let coin = Coin::Penny; let mut count = 0; if let Coin::Quarter(state) = coin { println!("State quarter from {:?}!", state); } else { count += 1; } }

 

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

Rust 专题






技术帖      match      switch      rust      控制流     


京ICP备2021035038号