]>
Commit | Line | Data |
---|---|---|
2c00a5a8 XL |
1 | # Wrapping errors |
2 | ||
3 | An alternative to boxing errors is to wrap them in your own error type. | |
4 | ||
5 | ```rust,editable | |
6 | use std::error; | |
923072b8 | 7 | use std::error::Error; |
2c00a5a8 XL |
8 | use std::num::ParseIntError; |
9 | use std::fmt; | |
10 | ||
11 | type Result<T> = std::result::Result<T, DoubleError>; | |
12 | ||
13 | #[derive(Debug)] | |
14 | enum DoubleError { | |
15 | EmptyVec, | |
16 | // We will defer to the parse error implementation for their error. | |
17 | // Supplying extra info requires adding more data to the type. | |
18 | Parse(ParseIntError), | |
19 | } | |
20 | ||
21 | impl fmt::Display for DoubleError { | |
22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
23 | match *self { | |
24 | DoubleError::EmptyVec => | |
25 | write!(f, "please use a vector with at least one element"), | |
1b1a35ee XL |
26 | // The wrapped error contains additional information and is available |
27 | // via the source() method. | |
28 | DoubleError::Parse(..) => | |
29 | write!(f, "the provided string could not be parsed as int"), | |
2c00a5a8 XL |
30 | } |
31 | } | |
32 | } | |
33 | ||
34 | impl error::Error for DoubleError { | |
532ac7d7 | 35 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
2c00a5a8 XL |
36 | match *self { |
37 | DoubleError::EmptyVec => None, | |
38 | // The cause is the underlying implementation error type. Is implicitly | |
39 | // cast to the trait object `&error::Error`. This works because the | |
40 | // underlying type already implements the `Error` trait. | |
41 | DoubleError::Parse(ref e) => Some(e), | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | // Implement the conversion from `ParseIntError` to `DoubleError`. | |
47 | // This will be automatically called by `?` if a `ParseIntError` | |
48 | // needs to be converted into a `DoubleError`. | |
49 | impl From<ParseIntError> for DoubleError { | |
50 | fn from(err: ParseIntError) -> DoubleError { | |
51 | DoubleError::Parse(err) | |
52 | } | |
53 | } | |
54 | ||
55 | fn double_first(vec: Vec<&str>) -> Result<i32> { | |
56 | let first = vec.first().ok_or(DoubleError::EmptyVec)?; | |
1b1a35ee XL |
57 | // Here we implicitly use the `ParseIntError` implementation of `From` (which |
58 | // we defined above) in order to create a `DoubleError`. | |
2c00a5a8 XL |
59 | let parsed = first.parse::<i32>()?; |
60 | ||
61 | Ok(2 * parsed) | |
62 | } | |
63 | ||
64 | fn print(result: Result<i32>) { | |
65 | match result { | |
66 | Ok(n) => println!("The first doubled is {}", n), | |
1b1a35ee XL |
67 | Err(e) => { |
68 | println!("Error: {}", e); | |
69 | if let Some(source) = e.source() { | |
70 | println!(" Caused by: {}", source); | |
71 | } | |
72 | }, | |
2c00a5a8 XL |
73 | } |
74 | } | |
75 | ||
76 | fn main() { | |
77 | let numbers = vec!["42", "93", "18"]; | |
78 | let empty = vec![]; | |
79 | let strings = vec!["tofu", "93", "18"]; | |
80 | ||
81 | print(double_first(numbers)); | |
82 | print(double_first(empty)); | |
83 | print(double_first(strings)); | |
84 | } | |
85 | ``` | |
86 | ||
87 | This adds a bit more boilerplate for handling errors and might not be needed in | |
88 | all applications. There are some libraries that can take care of the boilerplate | |
89 | for you. | |
90 | ||
91 | ### See also: | |
92 | ||
93 | [`From::from`][from] and [`Enums`][enums] | |
94 | ||
95 | [from]: https://doc.rust-lang.org/std/convert/trait.From.html | |
dc9dc135 | 96 | [enums]: ../../custom_types/enum.md |