使用 Box 处理错误

通过对错误类型实现 DisplayFrom,我们能够利用上绝大部分标准库错误处理工具。然而,我们遗漏了一个功能:轻松 Box 我们错误类型的能力。

标准库会自动通过 Form 将任意实现了 Error trait 的类型转换成 trait 对象 Box<Error> 的类型(原文:The std library automatically converts any type that implements the Error trait into the trait object Box<Error>, via From. )。对于一个库用户,下面可以很容易做到:

  1. fn foo(...) -> Result<T, Box<Error>> { ... }

用户可以使用一系列外部库,其中每个都提供各自错误类型。为了定义一个有效的 Result<T, E> 类型,用户有几个选择:

  • 定义一个新的限定在外部库错误类型的包装(wrapper)错误类型(原文:define a new wrapper error type around the libraries error types)
  • 将错误类型转换成 String 或者其他合适的选择
  • 通过类型擦除(type erasure)将错误类型装包(Box)成 Box<Error>

将内容“装包”(”Boxing”)是一个常见的选择。缺点是潜在的错误类型只能在运行时知道,且不能静态确定(statically determined)。正如刚才提到的,要做到这点所有要做的事情就是实现 Error trait:

  1. trait Error: Debug + Display {
  2. fn description(&self) -> &str;
  3. fn cause(&self) -> Option<&Error>;
  4. }

有了这个实现后,我们再来回顾前面学过的最近例子。注意到它所带的错误类型 Box<Error> 也变成有效的了,就像前面用到的 DoubleError 那样(原文:With this implementation, let’s look at our most recent example. Note that it is just as valid with the error type of Box<Error> as it was before with DoubleError):

  1. use std::error;
  2. use std::fmt;
  3. use std::num::ParseIntError;
  4. // 将别名更改为 `Box<error::Error>`。
  5. type Result<T> = std::result::Result<T, Box<error::Error>>;
  6. #[derive(Debug)]
  7. enum DoubleError {
  8. EmptyVec,
  9. Parse(ParseIntError),
  10. }
  11. impl From<ParseIntError> for DoubleError {
  12. fn from(err: ParseIntError) -> DoubleError {
  13. DoubleError::Parse(err)
  14. }
  15. }
  16. impl fmt::Display for DoubleError {
  17. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  18. match *self {
  19. DoubleError::EmptyVec =>
  20. write!(f, "please use a vector with at least one element"),
  21. DoubleError::Parse(ref e) => e.fmt(f),
  22. }
  23. }
  24. }
  25. impl error::Error for DoubleError {
  26. fn description(&self) -> &str {
  27. match *self {
  28. // 错误的简短说明。不需要和 `Display` 一样。
  29. DoubleError::EmptyVec => "empty vectors not allowed",
  30. // 这已经实现了 `Error`,所以遵循它自己的实现。
  31. DoubleError::Parse(ref e) => e.description(),
  32. }
  33. }
  34. fn cause(&self) -> Option<&error::Error> {
  35. match *self {
  36. // 没有潜在的差错,所以返回 `None`。
  37. DoubleError::EmptyVec => None,
  38. // 差错为底层实现的错误类型。被隐式地转换成 trait 对象 `&error::Error`。
  39. // 这会正常工作,因为底层的类型已经实现了 `Error` trait。
  40. DoubleError::Parse(ref e) => Some(e),
  41. }
  42. }
  43. }
  44. fn double_first(vec: Vec<&str>) -> Result<i32> {
  45. let first = try!(vec.first().ok_or(DoubleError::EmptyVec));
  46. let parsed = try!(first.parse::<i32>());
  47. Ok(2 * parsed)
  48. }
  49. fn print(result: Result<i32>) {
  50. match result {
  51. Ok(n) => println!("The first doubled is {}", n),
  52. Err(e) => println!("Error: {}", e),
  53. }
  54. }
  55. fn main() {
  56. let numbers = vec!["93", "18"];
  57. let empty = vec![];
  58. let strings = vec!["tofu", "93", "18"];
  59. print(double_first(numbers));
  60. print(double_first(empty));
  61. print(double_first(strings));
  62. }

参见:

Dynamic dispatchError trait