The Rust Programming Language:Error Handling
Rust 没有异常。相反,它具有可恢复错误的类型 Result<T, E> 和在程序遇到不可恢复错误时停止执行的 panic! 宏。
Unrecoverable Errors with panic!
有两种可以触发 panic 的方式:做能让代码触发 panic 的动作(例如数组越界)或显式地调用panic!宏。默认地,这些 panic 会打印错误信息,清理栈,然后退出。通过环境变量,你可以让 Rust 在 panic 的时候显示调用栈信息便于调试。
Unwinding the Stack or Aborting in Response to a Panic
默认情况下,当发生崩溃时,程序会开始展开,这意味着Rust会沿着栈往回走,并清理它遇到的每个函数中的数据。然而,往回走并清理是非常繁琐的工作。因此,Rust允许你选择立即中止的替代方案,这将结束程序而不进行清理。
程序使用的内存将需要由操作系统来清理。如果在你的项目中你需要使生成的二进制文件尽可能小,你可以通过在Cargo.toml文件中适当的[profile]部分添加panic = ‘abort’来从展开切换到在崩溃时中止。例如,如果你想在release下崩溃时中止,则添加以下内容:
1 | [profile.release] |
尝试在程序中调用panic!:
1 | fn main() { |
当运行程序时,你可以看到:
1 | $ cargo run |
另一个 case:
1 | fn main() { |
运行则会报错:
1 | $ cargo run |
如果设置 RUST_BACKTRACE=1:
1 | $ RUST_BACKTRACE=1 cargo run |
Recoverable Errors with Result
Result 枚举定义了两个枚举值:
1 | enum Result<T, E> { |
调用一个返回Result值的函数,因为这个函数可能会失败:
1 | use std::fs::File; |
用 match 来处理 Result:
1 | use std::fs::File; |
Matching on Different Errors
对于不同的错误进行不同的处理:
1 | use std::fs::File; |
Alternatives to Using
matchwithResult<T, E>
可以看到,每次都要用一大堆match来匹配每个 case。可以用闭包和unwrap_or_else函数结合的方式来更简易地实现:
1 | use std::fs::File; |
Shortcuts for Panic on Error: unwrap and expect
match 太冗长了,可以用快捷方法wrap,如果Result是Ok,那么它会返回Ok中包含的值;否则会为我们调用panic!宏:
1 | use std::fs::File; |
如果不存在这个文件,则会 panic:
1 | thread 'main' panicked at src/main.rs:4:49: |
类似地,expect方法也能让我们自定义panic!的错误信息:
1 | use std::fs::File; |
unwrap和expect的区别其实就是expect可以自定义 panic 信息、
此时的报错信息为:
1 | thread 'main' panicked at src/main.rs:5:10: |
Propagating Errors
1 | use std::fs::File; |
A Shortcut for Propagating Errors: The ? Operator
1 | use std::fs::File; |
Err 会提前 return。
与 match 不同的是:对于 err 的情况会调用在 From 特性上定义的 from 方法,这会将值从一个类型转换到另一个类型。当 ? 操作符调用了 from 函数,那么它就会将接收到的错误类型转换为方法中需要返回的错误类型。
还可以写得更简单:
1 | use std::fs::File; |
还能再简化:
1 | use std::fs; |
Where the ? Operator Can Be Used
如果我们在 main 函数中使用 ? 操作符:
1 | use std::fs::File; |
这里就不合适了,因为 ? 会返回一个 Result ,而 main 的返回值类型是 (),报错信息:
1 | $ cargo run |
错误信息中提到 ? 也可以被用于 Option<T>。跟在Result上使用 ? 一样,你也只能在返回 Option 的方法中使用 ?。它们的行为也是类似的,如果值是 None 那么就会提前返回 None,例如:
1 | fn last_char_of_first_line(text: &str) -> Option<char> { |
但是你不能混合使用 ? ,不能同时返回 Option 和 Result。
main 函数其实也可以返回 Result<(), E>,因此我们可以把上述代码修改为:
1 | use std::error::Error; |
Box<dyn Error> 是一个 trait object,这会在后面讲到。目前你可以认为 Box<dyn Error> 意味着任何类型的错误。