• 使用 Box 处理错误
    • 参见:

    使用 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 dispatch 和 Error trait