We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 16 of 24
Error chains and source()
Implement chain and rendered using the supplied error types and
build(depth) function. Keep their existing messages and source links.
pub fn chain(depth: usize) -> Vec<String>
pub fn rendered(depth: usize) -> String
chain returns each error's Display text, starting with the outer error and
following source() until it returns None. rendered joins those strings
with exactly ": ", with no leading or trailing separator.
Required output
The builder always creates a ConfigError. Its depth argument controls
which causes are attached:
| Depth | Layers, in output order |
|---|---|
0 or 1 |
ConfigError |
2 |
ConfigError, FieldError |
3 or greater |
ConfigError, FieldError, ParseIntError |
The exact messages are:
failed to load configurationkey `port` is not an integerinvalid digit found in string
For example, chain(3) returns these three strings in a vector. rendered(3)
returns:
failed to load configuration: key `port` is not an integer: invalid digit found in string
Both rendered(0) and rendered(1) return just
"failed to load configuration". Even usize::MAX produces only three
layers. Follow the links rather than using depth as the number of iterations.
The test wrapper reads depth and reports both functions' outputs.
What source() provides
Each error describes its own part of the failure. Here the outer message names the failed operation, the middle message identifies the field, and the parser explains why the value was rejected. The reporting code combines them without needing to know each concrete error type.
Display does not automatically walk the chain. These supplied implementations
leave their causes out of their own messages, so joining the layers does not
repeat the same information.
The starter calls top.sources(), which fails with E0599 on the exercise's
Rust 1.98.1 toolchain. The similarly named experimental sources() iterator
belongs to dyn Error; it is not a stable trait method available on every
concrete error. Use the stable source() method here. It returns one optional
cause, not the entire chain, and does not include the error you called it on.
Remember to record the top-level error too.
Keep the owning error alive
A source is borrowed from the error that exposes it. Binding the outer error to a variable keeps it available while you follow those borrows. This complete example walks a smaller two-layer chain:
use std::{error::Error, fmt, num::ParseIntError};
#[derive(Debug)]
struct SettingError(ParseIntError);
impl fmt::Display for SettingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid setting")
}
}
impl Error for SettingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0)
}
}
fn example_error() -> SettingError {
SettingError("12x".parse::<i32>().unwrap_err())
}
fn main() {
let top = example_error();
let mut messages = vec![top.to_string()];
let mut current = top.source();
while let Some(error) = current {
messages.push(error.to_string());
current = error.source();
}
assert_eq!(messages.join(": "), "invalid setting: invalid digit found in string");
}
If you instead store example_error().source() and use that borrowed source in
a later statement, the temporary owner has already been dropped. Rust rejects
that use with E0716. A named owner avoids this problem.
In Option<&(dyn Error + 'static)>, the outer reference is still tied to the
borrow of self. The 'static bound applies to the concrete error type, not
the duration of that borrow. The type may own its data or contain static
references; it cannot rely on references with shorter lifetimes.
Render after collecting
Vec<String> can be joined with join(": "). This naturally inserts separators
between messages and handles the one-message case without a trailing colon.
The supplied chains are finite and end at None; neither function needs to
return a Result for this task.
A cause chain describes related errors. A stack backtrace records call frames
and is a separate tool: stable Rust supports std::backtrace::Backtrace::capture()
without using the experimental Error::provide() API. No backtrace is needed
for this exercise.
Your solution must compile, match the outputs and pass Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.