![97790e9d2a6ea2926aec5011e4214f89.png](https://i-blog.csdnimg.cn/blog_migrate/b053f4321873e6fef28a9638fcce899b.jpeg)
函数式编程语言中是没有 null 的,或者说它是一个负效果。Rust 从中借鉴了这个概念,引入了 Option 类型来解决的 null 的问题。
下面我们来看看如何使用 Option 类型,这次我们用单元测试的方式来书写代码,首先创建一个库项目,执行下面的命令:
cargo new nonull —lib
进入创建好的 nonull 目录的 src 目录,我们会看到自动生成的 lib.rs 文件,里面是一个非常简单的单元测试,我们不需要它,用我们的代码替换它:
#[test]fn option_unwrap() { assert_eq!(Some(1).unwrap(), 1); // 1 assert_eq!(None.unwrap_or(1), 1); // 2 assert_eq!(None.unwrap_or_else(|| 2 * 3), 6); // 3 Option::::None.unwrap(); // 4 Option::::None.expect("这里提供出错信息"); // 5}
我们知道 Option 类型是 enum,它有两个值,Some 和 None,一个代表有值,一个代表没值,也就是我们在其他语言中常见的 null。我们使用 unwrap 方法把真正的值从 Some 中取出。unwrap_or 函数发现如果调用者是 None,就会返回参数中提供的值。unwrap_or_else 函数的参数为闭包,我们把它看成 lambda 表达式或者匿名函数即可。如果调用者是 None,则返回闭包中的值。在 None 上调用 unwrap 函数会发生 panick,即异常。在 Some 上调用 expect 函数,会返回 Some 中的值;在 None 上调用 expect 则会发生异常,但异常的信息则是 expect 函数的参数。
在命令行运行 cargo test 即可运行单元测试。
再来看看另一个单元测试函数:
#[test]fn option_examples() { let mut o = Some(1); let one = o.take(); // 1 assert!(o.is_none()); assert_eq!(one, Some(1)); let mut o = Some(1); assert_eq!(o.replace(2), Some(1)); // 2 assert_eq!(o, Some(2)); let o = Some(1); assert_eq!(o.map(|v| format!("{:#x}", v)), Some(String::from("0x1"))); // 3 let o = Some(1); match o.ok_or("出错") { // 4 Ok(one) => assert_eq!(one, 1), Err(_) => assert!(false), }}
take 函数运行后返回和自己一样的 Some,然后用 None 覆盖自己。注意使用 mut 关键字。replace 函数返回和自己一样的 Some,然后用参数替换原来 Some 中的值。map 函数可以改变 Some 中的值。这不容置疑,因为 Option 是个 Functor。ok_or 函数可以把 Option 转换为一个 Result。Result 代表正常返回或者异常,也是个 Functor。
下面看看更函数式的 Option 代码:
#[test]fn option_sequentials() { let a = Some(1); let b = Some(2); assert_eq!(a.and(b), Some(2)); // 1 assert_eq!(a.and(Option::::None), None); // 2 assert_eq!(a.or(None), Some(1)); // 3 assert_eq!(a.or(b), Some(1)); // 4 assert_eq!(None.or(a), Some(1)); // 5 let new_a = a.and_then(|v| Some(v + 100)).filter(|&v| v != 1); // 6 assert_eq!(new_a, Some(101)); let mut a_iter = new_a.iter(); // 7 assert_eq!(a_iter.next(), Some(&101)); assert_eq!(a_iter.next(), None);}
两个 Some 做 and 运算返回后面的那个 Some。Some 和 None 做 and 运算,返回 None。Some 和 None 做 or 运算,不管前后,都返回 Some。Some 和 Some 做 or 运算,返回前面的 Some。各种操作符,和 Rx 类似。Option 也有 iter 函数,可以转换为一个迭代器。
最后我们来看下 Option 的模式匹配:
#[test]fn option_pattern_matching() { match Some(1) { Some(v) => assert_eq!(v, 1), None => assert!(false), }; if let Some(v) = Some(2) { assert_eq!(v, 2); } else { assert!(false); }}
这个没什么可说的了,看代码演示即可。分别是 match 和 if let 匹配。
如有任何问题, 请添加微信公众号“读一读我”。