The standard error types take you far, but real programs define their own. A custom error type lets you enumerate exactly the ways your code can fail — and pattern-match on them like any enum.
An error enum
The idiomatic shape is an enum with one variant per failure mode: NotFound, Invalid(String), Io(...). Callers can match on the variant to react differently to each kind of failure — retry on a timeout, report on a parse error, give up on a missing file.
Wiring up ? with From
To let ? convert a lower-level error into your enum, implement From<ThatError> for YourError. Now any function returning Result<_, YourError> can use ? on operations that fail with ThatError, and the conversion happens automatically. This is the concrete payoff of the From trait from the Traits track.
Debug and implementing Display (a human message) plus the marker trait std::error::Error makes your type a well-behaved citizen — usable with Box<dyn Error>, printable, convertible. It's a little boilerplate, which is exactly what the crates in the next lesson remove.When to bother
A throwaway script can return Result<_, String> or Box<dyn Error> and move on. A library other people depend on should define a precise error enum, so callers can match and react. The size of the program — and who consumes the errors — decides how much structure is worth it.