1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
# Using `#[derive(Error)]`
Deriving `Error` will generate an `Error` implementation, that contains
(depending on the type) a `source()` and a `provide()` method. Please note,
at the time of writing `provide()` is only supported on nightly rust. So you
have to use that to make use of it.
For a struct, these methods always do the same. For an `enum` they have separate
behaviour for each of the variants. The variant is first matched and then the
implementation will do the same as it would have done if the variant was a
struct.
Usually when you derive `Error` you will also want to [derive `Display`](crate::Display) and
often [`From` as well](crate::From).
### When and how does it derive `source()`?
1. It's a struct/variant with named fields and one is the fields is
called `source`. Then it would return that field as the `source`.
2. It's a tuple struct/variant and there's exactly one field that is not used as
the `backtrace`. So either a tuple struct with one field, or one with two where one
is the `backtrace`. Then it returns this field as the `source`.
3. One of the fields is annotated with `#[error(source)]`. Then it would
return that field as the `source`.
### When and how does it derive `provide()`?
1. It's a struct/variant with named fields and one of the fields is
called `backtrace`. Then it would return that field as the `backtrace`.
2. It's a tuple struct/variant and the type of exactly one of the fields is
called `Backtrace`. Then it would return that field as the `backtrace`.
3. One of the fields is annotated with `#[error(backtrace)]`. Then it would
return that field as the `backtrace`.
### Ignoring fields for derives
It's possible to ignore a field or a whole enum variant completely for this
derive using the `#[error(ignore)]` attribute. This will ignore it both for
detecting `backtrace` and `source`. It's also possible to mark a field only
ignored for one of these methods by using `#[error(not(backtrace))]` or
`#[error(not(source))]`.
### What works in `no_std`?
If you want to use the `Error` derive on `no_std` environments, then you need to
compile with nightly and enable this feature:
```ignore
#![feature(error_in_core)]
```
Backtraces don't work though, because the `Backtrace` type is only available in
`std`.
## Example usage
```rust
# #![cfg_attr(nightly, feature(error_generic_member_access, provide_any))]
// Nightly requires enabling these features:
// #![feature(error_generic_member_access, provide_any)]
# #[cfg(not(nightly))] fn main() {}
# #[cfg(nightly)] fn main() {
# use std::{any, error::Error as _, backtrace::Backtrace};
#
# use derive_more::{Display, Error, From};
// std::error::Error requires std::fmt::Debug and std::fmt::Display,
// so we can also use derive_more::Display for fully declarative
// error-type definitions.
#[derive(Default, Debug, Display, Error)]
struct Simple;
#[derive(Default, Debug, Display, Error)]
struct WithSource {
source: Simple,
}
#[derive(Default, Debug, Display, Error)]
struct WithExplicitSource {
#[error(source)]
explicit_source: Simple,
}
#[derive(Default, Debug, Display, Error)]
struct Tuple(Simple);
#[derive(Default, Debug, Display, Error)]
struct WithoutSource(#[error(not(source))] i32);
#[derive(Debug, Display, Error)]
#[display("An error with a backtrace")]
struct WithSourceAndBacktrace {
source: Simple,
backtrace: Backtrace,
}
// derive_more::From fits nicely into this pattern as well
#[derive(Debug, Display, Error, From)]
enum CompoundError {
Simple,
WithSource {
source: Simple,
},
#[from(ignore)]
WithBacktraceFromSource {
#[error(backtrace)]
source: Simple,
},
#[display("{source}")]
WithDifferentBacktrace {
source: Simple,
backtrace: Backtrace,
},
WithExplicitSource {
#[error(source)]
explicit_source: WithSource,
},
#[from(ignore)]
WithBacktraceFromExplicitSource {
#[error(backtrace, source)]
explicit_source: WithSource,
},
Tuple(WithExplicitSource),
WithoutSource(#[error(not(source))] Tuple),
}
assert!(Simple.source().is_none());
assert!(any::request_ref::<Backtrace>(&Simple).is_none());
assert!(WithSource::default().source().is_some());
assert!(WithExplicitSource::default().source().is_some());
assert!(Tuple::default().source().is_some());
assert!(WithoutSource::default().source().is_none());
let with_source_and_backtrace = WithSourceAndBacktrace {
source: Simple,
backtrace: Backtrace::capture(),
};
assert!(with_source_and_backtrace.source().is_some());
assert!(any::request_ref::<Backtrace>(&with_source_and_backtrace).is_some());
assert!(CompoundError::Simple.source().is_none());
assert!(CompoundError::from(Simple).source().is_some());
assert!(CompoundError::from(WithSource::default()).source().is_some());
assert!(CompoundError::from(WithExplicitSource::default()).source().is_some());
assert!(CompoundError::from(Tuple::default()).source().is_none());
# }
```
|