魔幻的 rust enum 枚举类型

更新日期: 2024-05-09 阅读次数: 338 字数: 1154 分类: rust

在看一段 github 上文本解析库的 rust 代码时,看到这样一个 enum 的定义:

#[derive(Debug)]
pub enum Line {
    Comment(String),
    Metadata(String, String),
    Entry(DictEntry<String>),
    Empty,
    Incorrect,
}

我整个人直接蒙了,为啥每个 item 后面还能备注类型?(这个时候我还没联想到 Option)

早上, 上班路上翻了一下 rust 的官方文档,找到了答案 (比翻螃蟹书方便多了)。例如一个 IP 地址的存储结构,按照我旧有的 enum 理解,应该这样定义:

  • 一个 enum 枚举类型标识是 IP v4 还是 v6
  • 然后结构体中存储类型和具体值
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 可以简化类型的定义:

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

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

Where structs give you a way of grouping together related fields and data, like a Rectangle with its width and height; enums give you a way of saying a value is one of a possible set of values.

从 enum 的定义上看,确实没毛病,rust 把 enum 的使用做到了极致。。。

再回到最初的那段代码,看起来就顺眼多了,确实一行 txt 的文本记录,有可能是一条数据,也有可能是一行注释,或是其他信息。最终每一行被抽象成了 enum Line 中的一个子类型。

Rust 是如何通过 enum 解决空指针问题的

Rust 语言并不包含 Null 类型。主要是为了规避场景的空指针异常。

Rust doesn’t have the null feature that many other languages have. The problem with null values is that if you try to use a null value as a not-null value, you'll get an error of some kind.

Rust 是通过引入 Option 这个 enum 类型,来解决 Null 问题的。   我觉得 Option 的设计非常棒,配合上 match 匹配,比 Kotlin 的 nullable 类型加问号的写法,要严谨多了。

Option

a particularly useful enum, called Option, which expresses that a value can be either something or nothing.

Option 的定义:

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

match 做流程控制

先看看 match 的用法:

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

The power of match comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled.

match 相对于 if 的区别:

  • match 的条件可以是任何类型,而 if 只能是 bool 值
  • match 可以强制你处理 enum 中的所有情况,避免逻辑分支的遗漏。例如,如果不处理 None 的情况,编译时会报错:"pattern None not covered"

这样来看确实 github 上那个文本解析的库,基于 enum 的处理方式,更经验老道一些。写出了 rust 的味道。。。

注: Option::None 和 Option::Some 可以简写为 None / Some, 因为这两个类型会被自动引入。

match 中的偷懒处理

如果 enum 情况太多,懒得处理怎么办,可以使用 other 或者下划线来做默认处理,类似 else。

match dice_roll {
    3 => add_fancy_hat(),
    7 => remove_fancy_hat(),
    other => move_player(other),
}

match dice_roll {
    3 => add_fancy_hat(),
    7 => remove_fancy_hat(),
    _ => (),
}

这里的区别是 other 是能获取到对应的值,而下划线则值都忽略了。

注:这个空括号,类似 Kotlin 里的 Unit,即其他语言中的 void。

The tuple without any values has a special name, unit. This value and its corresponding type are both written () and represent an empty value or an empty return type. Expressions implicitly return the unit value if they don’t return any other value.

还有一种偷懒的写法,即 if let,其只处理 let 后面的一种情况。

let config_max = Some(3u8);
if let Some(max) = config_max {
    println!("The maximum is configured to be {}", max);
}

也支持 else 写法,跟下划线的逻辑是一样的。

参考

https://doc.rust-lang.org/book/ch06-00-enums.html

The Rust Programming Language 这个教程中讲解的 enum 由浅入深,非常透彻。 不愧是官方推荐的教程。

感悟

  • 还是读官方推荐的英文教程比较好理解,看中文的教程反而看不懂。
  • rust 开发文本解析程序,虽然看起来比 python 这类脚本语言啰嗦,但是带来的性能提升,及逻辑严谨度提升,确实是值得的。
  • 多找些用到的 github 上的 rust 库代码看看,能学到不少地道的 rust 代码 (rust pattern),比自己蒙头写提升太多了。

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式