Option 与 Result 是 Rust 处理「可能没有」和「可能失败」的方式。这篇从最底层讲起:它们只是普通枚举,没有魔法;编译器怎样拒绝把 Option 当值用;一组常用方法实际怎么工作;panic 时信息长什么样;以及它们占多少内存 —— 为什么 Option<&T> 是免费的。
源码:
1 | #[allow(dead_code)]
2 | enum MyOption<T> { None, Some(T) }
3 | #[allow(dead_code)]
4 | enum MyResult<T, E> { Ok(T), Err(E) }
5 | fn first_even(v: &[i32]) -> MyOption<i32> { for &x in v { if x % 2 == 0 { return MyOption::Some(x); } } MyOption::None }
6 | fn main() {
7 | match first_even(&[1, 3, 4, 5]) { MyOption::Some(x) => println!("找到偶数 {x}"), MyOption::None => println!("没有偶数") }
8 | // 标准库的版本
9 | let o: Option<i32> = [1, 3, 4, 5].iter().copied().find(|x| x % 2 == 0);
10 | let e: Result<i32, std::num::ParseIntError> = "12x".parse::<i32>();
11 | println!("{:?} {:?}", o, e);
12 | }
编译:成功
运行(退出码 0):
找到偶数 4
Some(4) Err(ParseIntError { kind: InvalidDigit }) 源码:
1 | fn main() {
2 | let a: Option<i32> = Some(5);
3 | let b = a + 1;
4 | println!("{b}");
5 | }
编译:失败
error[E0369]: cannot add `{integer}` to `Option<i32>`
--> <实验目录>/src/u10b.rs:3:15
|
3 | let b = a + 1;
| - ^ - {integer}
| |
| Option<i32>
|
note: `Option<i32>` does not implement `Add<{integer}>`
--> <rust 标准库源码>/library/core/src/option.rs:598:1
|
598 | pub enum Option<T> {
| ^^^^^^^^^^^^^^^^^^ `Option<i32>` is defined in another crate
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0369`. 源码:
1 | fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> { let n: i32 = s.parse()?; Ok(n * 2) }
2 | fn main() {
3 | let a = Some(3); let n: Option<i32> = None;
4 | println!("unwrap_or:{} {}", a.unwrap_or(0), n.unwrap_or(0));
5 | println!("map:{:?} {:?}", a.map(|x| x * 10), n.map(|x| x * 10));
6 | println!("and_then:{:?}", a.and_then(|x| if x > 2 { Some(x + 100) } else { None }));
7 | println!("ok_or:{:?} {:?}", a.ok_or("没有值"), n.ok_or("没有值"));
8 | println!("Result 转 Option:{:?} {:?}", "7".parse::<i32>().ok(), "x".parse::<i32>().ok());
9 | println!("?:{:?} {:?}", parse_and_double("21"), parse_and_double("abc"));
10 | println!("filter:{:?} {:?}", a.filter(|x| x % 2 == 1), a.filter(|x| x % 2 == 0));
11 | println!("take:{:?}", { let mut t = Some(9); let old = t.take(); (old, t) });
12 | }
编译:成功
运行(退出码 0):
unwrap_or:3 0
map:Some(30) None
and_then:Some(103)
ok_or:Ok(3) Err("没有值")
Result 转 Option:Some(7) None
?:Ok(42) Err(ParseIntError { kind: InvalidDigit })
filter:Some(3) None
take:(Some(9), None)? 的展开方式见? 运算符展开过程;错误传播见Result 错误传播。
编译:成功
运行(退出码 101):
先尝试 expect:
thread 'main' (6938666) panicked at <实验目录>/src/u10d.rs:4:15:
配置里的端口必须是整数: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace什么时候该 panic、什么时候该返回 Result,见panic!、unwrap 与 expect。
编译:成功
运行(退出码 0):
&u64 = 8,Option<&u64> = 8
Box<u64> = 8,Option<Box<u64>> = 8
i32 = 4,Option<i32> = 8
u64 = 8,Option<u64> = 16
Result<u8, u8> = 2,Result<u32, String> = 24
String = 24,Option<String> = 24niche 优化的原理见Option<&T> 的空指针优化。