We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 19 of 24
Adding context without anyhow
Repair the supplied WithContext implementation, then add a render helper that includes each error's source. Update load to use render on failure. Preserve ConfigError, its Display/source implementations and the public API:
pub trait WithContext<T> {
fn context(self, msg: &str) -> Result<T, ConfigError>;
}
pub fn load(pairs: Vec<(String, String)>, keys: Vec<String>) -> Result<Vec<i32>, String>
Lookup and parsing rules
Process keys in request order, including repeated keys. For each request:
- Find the first pair whose key matches exactly. Do not trim, lowercase or normalize keys. Later pairs with the same key do not replace the first.
- Trim Unicode whitespace from both ends of the selected value and parse it as an i32. Decimal ASCII digits may have a leading plus or minus and leading zeros. Internal whitespace, underscores, missing digits and values outside the i32 range fail.
- Append a successful value, or stop at the first failure and return Err.
Empty requests return Ok with an empty vector, even if pairs contains invalid values. Unrequested pairs are ignored. Empty and Unicode keys are valid; messages include keys exactly, including any punctuation or whitespace.
For pairs [("a", " -12 "), ("a", "99")] and requests ["a", "a"],
return Ok([-12, -12]). For a missing key, return its one-link message:
key `retries` is missing
For a present key whose value fails parsing, return context followed by the standard parser's message, separated by a colon and space:
key `port` is invalid: invalid digit found in string
An empty trimmed value instead has the cause cannot parse integer from empty string. Obtain causes from the actual error's Display implementation; do not
hardcode one parse message for every failure. Standard-library diagnostic
wording can depend on the toolchain.
The wrapper reports ok, values and error. Success has an empty error string; failure has an empty values array, even if earlier requests succeeded.
Add context while retaining the error
WithContext is a local extension trait implemented for Result<T, E>. Its context method should leave Ok(T) unchanged. For Err(E), create ConfigError::Context with an owned copy of msg and the original error boxed as source. map_err expresses that transformation without requiring T or E to implement Clone.
The call site knows which key failed, while ParseIntError knows why parsing failed. Keeping both makes the report useful. This is a small standard-library context wrapper, not a replacement for every feature of an error-reporting crate.
Implementing a local trait for the standard Result type is allowed. You are adding a trait method, not an inherent method to Result. The standard types remain foreign even when the exercise is compiled as one source file.
Why the error type needs a 'static bound
ConfigError stores Box<dyn Error + 'static>. To place E into that field,
the implementation needs E: Error + 'static. The starter omits the latter
bound and fails with E0310.
E: 'static means E cannot depend on references with shorter lifetimes. It does not mean the value lives forever, and it does not forbid references to static data. An owned String or an error holding a string literal can satisfy the bound and still be dropped normally. Boxing or type erasure by itself does not require 'static; this field's declared contract does.
This complete example shows a valid error holding a static reference:
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct Note(&'static str);
impl fmt::Display for Note {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl Error for Note {}
fn main() {
let error: Box<dyn Error + 'static> = Box::new(Note("missing setting"));
assert_eq!(error.to_string(), "missing setting");
drop(error);
}
Render each link once
Start with the top error's to_string(). Follow Error::source repeatedly until
it returns None, collecting each source's to_string() in order. Join those
messages with ": ". A source-less error produces just its own message.
The supplied error chains terminate; no cycle-handling policy is needed here.
ConfigError::Context's Display prints only msg, and source exposes the boxed cause. Keep that separation: adding the cause to Display while also walking source would duplicate it. For nested contexts, render every link rather than stopping after the first source. See the Error source documentation.
Fixing the bound alone leaves the starter's second defect: load currently returns only e.to_string(), losing the lower-level message in its output. Replace that reporting step with your chain renderer.
Passing requires the specified results, successful compilation and 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.