?main 和 tests 中

Minimum Rust version: 1.26

Rust的错误处理围绕返回 Result <T,E> 并使用 ? 传播错误。 对于那些编写许多小程序并且希望进行许多测试的人来说,更关注于那些复杂的入口,例如main#[test]中的错误处理。

举个例子,你将尝试这样写:

  1. use std::fs::File;
  2. fn main() {
  3. let f = File::open("bar.txt")?;
  4. }

因为 ? 通过处理 Result 并提前返回函数来工作,所以上面的代码不起作用,并且导致以下错误:

  1. error[E0277]: the `?` operator can only be used in a function that returns `Result`
  2. or `Option` (or another type that implements `std::ops::Try`)
  3. --> src/main.rs:5:13
  4. |
  5. 5 | let f = File::open("bar.txt")?;
  6. | ^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
  7. |
  8. = help: the trait `std::ops::Try` is not implemented for `()`
  9. = note: required by `std::ops::Try::from_error`

在 Rust 2015 中,处理这种问题,需要这样:

  1. // Rust 2015
  2. # use std::process;
  3. # use std::error::Error;
  4. fn run() -> Result<(), Box<Error>> {
  5. // real logic..
  6. Ok(())
  7. }
  8. fn main() {
  9. if let Err(e) = run() {
  10. println!("Application error: {}", e);
  11. process::exit(1);
  12. }
  13. }

但是,在这种情况下,run 函数具有所有有趣的逻辑,而 main 只是样板。 问题更糟糕的是 #[test],因为它们往往会有更多这种情况。

在 Rust 2018 中,你可以使得你的 #[test]main 函数返回一个 Result

  1. // Rust 2018
  2. use std::fs::File;
  3. fn main() -> Result<(), std::io::Error> {
  4. let f = File::open("bar.txt")?;
  5. Ok(())
  6. }

在这种情况下,如果说文件不存在并且某处有一个 Err(err),那么 main 将以错误代码(不是0)退出并打印出 Debug 表示 err

更多的细节

使 - > Result <..>main#[test] 的上下文中工作并不神奇。 它全部由 Termination 特征支持,所有有效的返回类型的 main 和测试函数必须实现。 特征定义为:

  1. pub trait Termination {
  2. fn report(self) -> i32;
  3. }

在为应用程序设置入口点时,编译器将使用此特征并在您编写的 main 函数的 Result 上调用 .report()

Result() 的这个特性的两个简化示例实现是:

  1. # #![feature(process_exitcode_placeholder, termination_trait_lib)]
  2. # use std::process::ExitCode;
  3. # use std::fmt;
  4. #
  5. # pub trait Termination { fn report(self) -> i32; }
  6. impl Termination for () {
  7. fn report(self) -> i32 {
  8. # use std::process::Termination;
  9. ExitCode::SUCCESS.report()
  10. }
  11. }
  12. impl<E: fmt::Debug> Termination for Result<(), E> {
  13. fn report(self) -> i32 {
  14. match self {
  15. Ok(()) => ().report(),
  16. Err(err) => {
  17. eprintln!("Error: {:?}", err);
  18. # use std::process::Termination;
  19. ExitCode::FAILURE.report()
  20. }
  21. }
  22. }
  23. }

正如您在 () 中看到的那样,只返回成功代码。 在 Result 的情况下,成功的话交给 () 来执行,错误的话,交给 Err(..),打印出错误消息并退出代码。

要了解有关更细节的信息,请参阅跟踪问题 或者 the RFC.