把错误 “装箱”

如果又想写简单的代码,又想保存原始错误信息,一个方法是把它们装箱Box)。这 样做的坏处就是,被包装的错误类型只能在运行时了解,而不能被静态地 判别

对任何实现了 Error trait 的类型,标准库的 Box 通过 From 为它们提供了 到 Box<Error> 的转换。

  1. use std::error;
  2. use std::fmt;
  3. // 为 `Box<error::Error>` 取别名。
  4. type Result<T> = std::result::Result<T, Box<dyn error::Error>>;
  5. #[derive(Debug, Clone)]
  6. struct EmptyVec;
  7. impl fmt::Display for EmptyVec {
  8. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  9. write!(f, "invalid first item to double")
  10. }
  11. }
  12. impl error::Error for EmptyVec {
  13. fn description(&self) -> &str {
  14. "invalid first item to double"
  15. }
  16. fn cause(&self) -> Option<&dyn error::Error> {
  17. // 泛型错误。没有记录其内部原因。
  18. None
  19. }
  20. }
  21. fn double_first(vec: Vec<&str>) -> Result<i32> {
  22. vec.first()
  23. .ok_or_else(|| EmptyVec.into()) // 装箱
  24. .and_then(|s| {
  25. s.parse::<i32>()
  26. .map_err(|e| e.into()) // 装箱
  27. .map(|i| 2 * i)
  28. })
  29. }
  30. fn print(result: Result<i32>) {
  31. match result {
  32. Ok(n) => println!("The first doubled is {}", n),
  33. Err(e) => println!("Error: {}", e),
  34. }
  35. }
  36. fn main() {
  37. let numbers = vec!["42", "93", "18"];
  38. let empty = vec![];
  39. let strings = vec!["tofu", "93", "18"];
  40. print(double_first(numbers));
  41. print(double_first(empty));
  42. print(double_first(strings));
  43. }

参见:

动态分发 and Error trait