Rust Option 模式匹配简介 - 图1

2021.05.23 16:26:25 字数 1,014 阅读 429

Option

Option 是 rust 非常好用的数据结构,用来解决 Null 空指针问题,是 rust 安全基石重要一环。其本质是一个 Enum 结构。本文对 option 进行模式匹配用于获取 option 包装的值的简单用法。

Option 的声明:

  1. pub enum Option<T> {
  2. None,
  3. Some(T),
  4. }

例子:

  1. let opt = Some("hello".to_string());
  2. println!("{:?}", opt);

模式匹配

Option 是一个 Enum,通过模式匹配获取其变体

Some(T) 变体

  1. let opt = Some("hello".to_string());
  2. match opt {
  3. Some(x) => println!("Some: x={}", x),
  4. None => println!("None")
  5. }

None 变体

  1. let opt:Option<String> = None;
  2. match opt {
  3. Some(x) => println!("Some: x={}", x),
  4. None => println!("None")
  5. }

变量 opt 可以是 None 变体。上面的 opt 需要指定类型,不然这段代码编译器无法推断 x 的类型。

unwarp 方法

Option 有很多有用的方法。unwarp 方法用于获取 Some(x) 中 的 x 值。如果 Option 是 None 变体,则该方法会 pannic。

  1. let opt = Some("hello".to_string());
  2. let s = opt.unwrap();
  3. println!("{}", s);

opt 通过 unwarp 方法获取变体 Some(x) 中的 x。若 opt 是 None 变体,unwarp 方法会 pannic

uwranp 的源码:

  1. pub const fn unwrap(self) -> T {
  2. match self {
  3. Some(val) => val,
  4. None => panic!("called `Option::unwrap()` on a `None` value"),
  5. }
  6. }

从 unwarp 的源码可以看出,它本质也是模式匹配的一种简写。

所有权

Option 的模式匹配和 unwarp 方法涉及所有权 move 语义。(x 指没有实现 Copy trait 的类型)

就像赋值,参数传递一样。直接模式匹配会涉及所有权 move

  1. let opt = Some("hello".to_string());
  2. match opt {
  3. Some(x) => println!("Some: x={}", x),
  4. None => println!("None")
  5. }
  6. println!("{:?}", opt);

上面的代码会编译错误。错误信息如下:

  1. error[E0382]: borrow of partially moved value: `opt`
  2. --> src/main.rs:54:22
  3. |
  4. 50 | Some(x) => println!("Some: x={}", x),
  5. | - value partially moved here
  6. ...
  7. 54 | println!("{:?}", opt);
  8. | ^^^ value borrowed here after partial move
  9. |
  10. = note: partial move occurs because value has type `String`, which does not implement the `Copy` trait
  11. help: borrow this field in the pattern to avoid moving `opt.0`

String 类型是没有实现 Copy trait,它存储在堆上。创建 opt 的时候,它的所有权 move 到 opt,通过模式匹配,所有权 move 到 x。x 在 match 花括号的作用域后 drop 了。但是 opt 没有所有权,再次打印会报错。

unwrap 实现基于模式匹配,因此 unwarp 方法也会 move 所有权。

  1. let opt = Some("hello".to_string());
  2. let s = opt.unwrap();
  3. println!("{:?}", opt);

编译错误信息:

  1. 48 | let opt = Some("hello".to_string());
  2. | --- move occurs because `opt` has type `Option<String>`, which does not implement the `Copy` trait
  3. 49 | let s = opt.unwrap();
  4. | -------- `opt` moved due to this method call
  5. 50 |
  6. 51 | println!("{:?}", opt);
  7. | ^^^ value borrowed here after move
  8. |

引用

move 语义会转移所有权,使用借用 borrow 语义就能保持所有权。

写法一

  1. let opt = Some("hello".to_string());
  2. match &opt {
  3. Some(x) => println!("{}", x),
  4. None => println!("None"),
  5. }
  6. println!("{:?}", opt);

写法二

  1. let opt = Some("hello".to_string());
  2. match opt {
  3. Some(ref x) => println!("{}", x),
  4. None => println!("None"),
  5. }
  6. println!("{:?}", opt);

opt 依然是正常的形式,不是其引用,在 Some 中使用 ref 修饰 x,此时 x 是 &String。 即将 opt 所有的 String 的引用借给 x

  1. let opt = Some("hello".to_string());
  2. let s = &opt.unwrap();
  3. println!("{:?}", opt);

很不幸,这样还是会编译错误。&opt.unwrap(); 实际是 &(opt.unwrap)。所有权 move 之后再取引用。那么很容易想到下面的做法

  1. let opt = Some("hello".to_string());
  2. let s = (&opt).unwrap();
  3. println!("{:?}", opt);

这样做依然会编译失败。即使是 &opt,unwarp 的签名是 self ,也就是 传递给 unwrap 的是 opt ,而不是 &opt。所有权还是转移了。想要实现 所有权的借用,可以仿照 上面 match 表达式的写法。

  1. let opt = Some("hello".to_string());
  2. let s = unwrap(&opt);
  3. println!("{:?}", s);
  4. println!("{:?}", opt);
  5. fn unwrap(opt: &Option<String>) -> &String {
  6. match opt {
  7. Some(x) => x,
  8. None => panic!("called `Option::unwrap()` on a `None` value"),
  9. }
  10. }

as_ref

既然我们能想到封装一个 unwrap 函数,标准库早也想到了。option 的 as_ref 源码

  1. pub const fn as_ref(&self) -> Option<&T> {
  2. match *self {
  3. Some(ref x) => Some(x),
  4. None => None,
  5. }
  6. }

上面的 Some(ref x) => Some(x) 就是我们上面展示的引用的写法二。过 as_ref 调用得到的是 Option<&String>。再调用 unwrap 方法,就是对其进行模式匹配,就是写法二的方式:

  1. let opt = Some("hello".to_string());
  2. let opt1 = opt.as_ref();
  3. match opt1 {
  4. Some(x) => println!("{}", x),
  5. None => println!("None"),
  6. }
  7. println!("{:?}", opt);
  8. println!("{:?}", opt1);

上面的过程可以连起来写成一行

  1. let opt = Some("hello".to_string());
  2. let s = opt.as_ref().unwrap();
  3. println!("{:?}", s);
  4. println!("{:?}", opt);

总结

Option 是 rust 类型安全重要思想的体现之一。它本质是一个 Enum 类型,有两个变体,Some(x) 和 None。当表示没有值的时候,可以使用 None。其语义类似其他语言如 Python 的 None,Golang 的 nil, java 的 null。但是又跟其他语言有本质的不同。rust 的 None 是 Option 类型。而不是 其他任何类型。而其他语言的 None nil 可以是任何其他类型或引用类型。Null 可以是 字串,也可以是 指针。这就埋下了很多安全隐患。

Rust 的中表示可有可无的时候使用 Option。有值的时候需要使用 Some 变体。解出 Some(x) 中的 x 值方法是模式匹配。同时标注库也提供了便捷方法如 unwrap。

无论是使用 模式匹配 还是一些方法,对于所有权的 move 还是 borrow 严格遵循 rust 的所有权系统。通过上面介绍的几个例子用以说明。

更多精彩内容,就在简书 APP

“小礼物走一走,来简书关注我”

还没有人赞赏,支持一下

Rust Option 模式匹配简介 - 图2

总资产 560 共写了 20.6W 字获得 2,738 个赞共 2,391 个粉丝

推荐阅读更多精彩内容

  • 模式匹配 模式匹配通常由一下组件组成: 字面量。解构的数组、枚举、结构体或者元组。变量。通配符占位符。 知识汇总 …

  • 1、匹配模式 Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:如 switch 语句、类型检查等。并且 Sca…

  • 1、匹配模式 Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:如 switch 语句、类型检查等。并且 Sca…

  • 1、匹配模式 Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:如 switch 语句、类型检查等。并且 Sca…

  • 哈里 · 基恩想和新教练何塞 · 穆里尼奥建立一种 “牢固的关系”,这将有助于托特纳姆更上一层楼。 凯恩在 4-2 战胜奥林匹亚…
    https://www.jianshu.com/p/974d83a1158e