Error Handling with Box<dyn Error>
is a boxed trait object that can hold any error type, letting a single Result propagate many different kinds of failure through the ? operator with almost no boilerplate.
Learn Error Handling with Box<dyn Error> in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll use with ? , implement Error + Display + Debug for a custom error, return a Result from main , and weigh it against enum errors.
What You'll Learn in This Lesson
1️⃣ One Result, Many Error Types
A function returning can fail in many ways. The ? operator unwraps an Ok or returns early on an Err , automatically converting the concrete error (here a ParseIntError ) into the boxed trait object via the From trait. You print the error with {' '} because every error implements Display .
The beauty is that you never wrote any conversion code — the single ? after text.parse() did the boxing. Add another fallible call with a different error type and the same ? works there too.
2️⃣ Defining Your Own Error
To create a custom error, define a type and give it three impls: Debug (usually derived), Display for the message, and std::error::Error (often an empty block). Once those are in place, your error slots into anywhere, and ? will box it automatically.
Debug is required by the Error trait (it's what gets printed if the error reaches main ), and Display is the friendly message you control.
3️⃣ Returning Result from main
main itself can return . That lets you use ? right inside main : on success you return Ok(()) , and on an error the program exits with a non-zero status and prints the error's Debug form.
This is the idiomatic shape of small Rust programs: helper functions return , main does too, and ? threads errors all the way to the top.
Your turn. Fill in the blank marked ___ , then run it.
Write a function that can fail two different ways — a parse error and a divide-by-zero — both flowing through one . Run it with cargo run and check the output.
📋 Quick Reference — Box<dyn Error>
Practice quiz
What is a Box<dyn Error>?
- A fixed error enum
- A panic handler
- A heap-allocated trait object that can hold any type implementing the Error trait
- A stack-only value
Answer: A heap-allocated trait object that can hold any type implementing the Error trait. It is a boxed trait object: any std::error::Error type behind a pointer, with the concrete type erased.
What does the dyn keyword signify in Box<dyn Error>?
- The concrete type is decided at runtime (dynamic dispatch)
- The error is dynamic-sized only
- The error is dangerous
- The value is never dropped
Answer: The concrete type is decided at runtime (dynamic dispatch). dyn marks a trait object whose concrete type is resolved at runtime, so one Result can carry many error types.
On an Err, before returning, what does the ? operator do to the error?
- Ignores it
- Panics
- Logs it to a file
- Converts it using the From trait
Answer: Converts it using the From trait. ? converts the error via From, which is why a concrete error like ParseIntError boxes into Box<dyn Error> automatically.
Which three things must a custom error type provide?
- new, drop, clone
- Debug, Display, and impl Error
- Copy, Default, Hash
- Iterator, Send, Sync
Answer: Debug, Display, and impl Error. A custom error needs Debug (required by Error), Display for the message, and an impl of the Error trait.
The Error trait requires which other trait to be implemented?
- Debug
- Clone
- Copy
- Default
Answer: Debug. Debug is required by the Error trait; it is what prints if the error reaches main.
What does Display provide for a custom error?
- The Debug braces output
- A clone method
- The human-readable message
- The error code
Answer: The human-readable message. Display defines the friendly, human-readable message you control.
Can a plain &str be turned into a Box<dyn Error>?
- No, never
- Yes, e.g. return Err("boom".into())
- Only with a macro
- Only for u32 errors
Answer: Yes, e.g. return Err("boom".into()). A &str or String already converts into Box<dyn Error>, so Err("boom".into()) works for quick errors.
What signature lets you use ? directly inside main?
- fn main()
- fn main() -> i32
- fn main() -> Option<()>
- fn main() -> Result<(), Box<dyn Error>>
Answer: fn main() -> Result<(), Box<dyn Error>>. When main returns Result<(), Box<dyn Error>> you can use ? at the top level; success returns Ok(()).
When main returns Result and errors out, what is printed and what is the exit status?
- The Display form, exit 0
- The Debug form of the error, non-zero exit
- Nothing, exit 0
- A stack trace, exit 1 always with no error
Answer: The Debug form of the error, non-zero exit. On Err, the program prints the error's Debug form and exits with a non-zero status.
When is Box<dyn Error> preferred over a concrete enum error?
- In libraries needing exact types
- When you must match on each variant
- In application code and binaries that mostly propagate errors to main
- Never
Answer: In application code and binaries that mostly propagate errors to main. Use Box<dyn Error> in apps for short signatures; use enum errors in libraries where callers inspect specific failures.