Rust 中处理异常的一种方式是 Result 作为函数的返回值,和 panic 相对的,Result 通常意味着希望你处理异常并恢复运行。
Result 的定义如下:
1 2 3 4
| enum Result<T, E> { Ok(T), Err(E), }
|
看起来有点像 go 的函数返回值,但是不同的是因为是 enum,所以 Ok 和 Err 是二选一的,而且这俩都是泛型,可以进行类型推断。
Err 比较复杂时,可以被 match 处理,例如 match 枚举值,但是这应该不是一个好的写法,看着晕晕的。而且这意味着你得知道 Result 里的 E 泛型到底实现了哪个 Error,看起来这里是返回了一个 std::io::Error, 谁知道其他地方会返回啥呢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| use std::fs::File; use std::io::ErrorKind;
fn main() { let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => { panic!("Problem opening the file: {:?}", other_error); } }, }; }
|
和 Option 一样,std 也给 Result 实现了一大堆方法,比较典型的是 unwrap 和 expect,这样当遇到 Err 的时候就直接 panic 了。
例如上面这段代码还可以这么写:这里似乎用了一个 lambda 语法,就是不知道这个写法要到哪里才会介绍。另外,注意 File::create 实际上是有返回值的,最后没有用分号,把这个作为新的返回值给到 greeting_file 了,不知道 rust 为什么要用这么隐晦的方式来写返回值,可能这就是对 IDE 的信任把。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use std::fs::File; use std::io::ErrorKind;
fn main() { let greeting_file = File::open("hello.txt").unwrap_or_else(|error| { if error.kind() == ErrorKind::NotFound { File::create("hello.txt").unwrap_or_else(|error| { panic!("Problem creating the file: {:?}", error); }) } else { panic!("Problem opening the file: {:?}", error); } }); }
|
另一个重要的语法糖就是 Error 的传播,适用于一连串操作,多次返回 Error 的情况,这在进行 IO 或是一步操作时非常常见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| use std::fs::File; use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> { let username_file_result = File::open("hello.txt");
let mut username_file = match username_file_result { Ok(file) => file, Err(e) => return Err(e), };
let mut username = String::new();
match username_file.read_to_string(&mut username) { Ok(_) => Ok(username), Err(e) => Err(e), } }
|
这种常见的操作可以用 ?
operator 进行简化:
1 2 3 4 5 6 7 8 9
| use std::fs::File; use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> { let mut username_file = File::open("hello.txt")?; let mut username = String::new(); username_file.read_to_string(&mut username)?; Ok(username) }
|
对上面这个操作进行进一步简化以后就是我们常见的 optional chaining:
1 2 3 4 5 6 7 8 9 10 11
| use std::fs::File; use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> { let mut username = String::new();
File::open("hello.txt")?.read_to_string(&mut username)?;
Ok(username) }
|
而?
操作符并没有那么简单,它还可以被用于返回 Option 类型的函数:
1 2 3 4
| fn last_char_of_first_line(text: &str) -> Option<char> { text.lines().next()?.chars().last() }
|
上面这个 next()返回了 Option 类型,但仍然可以使用?操作符来获取 Option 中的值, 这里如果?
操作符终端,None 会被提前返回,而不是继续执行后续的语句,因此?
只能被用在最后一个 expression 或是 return 语句里面。
?
到底是啥意思取决于当前函数的返回值,而返回值必须被手动指定而不能被推导出来,所以?
的行为是明确的。
不过拷贝代码的时候需要考虑到同一段代码实际上再不同的函数里 有不同的含义。