Boxing errors

A way to write simple code while preserving the original errors is to Box
them. The drawback is that the underlying error type is only known at runtime and not
statically determined.

The stdlib helps in boxing our errors by having Box implement conversion from
any type that implements the Error trait into the trait object Box<Error>,
via From.

  1. use std::error;
  2. use std::fmt;
  3. use std::num::ParseIntError;
  4. // Change the alias to `Box<error::Error>`.
  5. type Result<T> = std::result::Result<T, Box<error::Error>>;
  6. #[derive(Debug, Clone)]
  7. struct EmptyVec;
  8. impl fmt::Display for EmptyVec {
  9. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  10. write!(f, "invalid first item to double")
  11. }
  12. }
  13. impl error::Error for EmptyVec {
  14. fn description(&self) -> &str {
  15. "invalid first item to double"
  16. }
  17. fn cause(&self) -> Option<&error::Error> {
  18. // Generic error, underlying cause isn't tracked.
  19. None
  20. }
  21. }
  22. fn double_first(vec: Vec<&str>) -> Result<i32> {
  23. vec.first()
  24. .ok_or_else(|| EmptyVec.into()) // Converts to Box
  25. .and_then(|s| s.parse::<i32>()
  26. .map_err(|e| e.into()) // Converts to Box
  27. .map(|i| 2 * i))
  28. }
  29. fn print(result: Result<i32>) {
  30. match result {
  31. Ok(n) => println!("The first doubled is {}", n),
  32. Err(e) => println!("Error: {}", e),
  33. }
  34. }
  35. fn main() {
  36. let numbers = vec!["42", "93", "18"];
  37. let empty = vec![];
  38. let strings = vec!["tofu", "93", "18"];
  39. print(double_first(numbers));
  40. print(double_first(empty));
  41. print(double_first(strings));
  42. }

See also:

Dynamic dispatch and Error trait