weichengyi.com

Rust's ? is not exception handling

11 February 2026 · rust

People arriving from languages with exceptions read ? as a throw, and then expect something up the stack to catch it. Nothing does. There is no catch, no unwinding, no handler search.

expr? expands to roughly this:

match expr {
    Ok(v)  => v,
    Err(e) => return Err(From::from(e)),
}

That is the whole feature. An early return, plus a From conversion on the error type.

Why that distinction pays

Every propagation point is visible in the source. A function that can fail says so in its return type, and every place a failure travels through is marked with a character you can grep for. There is no action at a distance, and no invisible control flow.

It also means the cost is a branch and a move, not a stack walk. Errors being cheap is why Rust code returns Result for ordinary, expected failures instead of reserving it for catastrophes.

The From conversion is the part worth learning

It is what lets a function returning Result<T, MyError> use ? on an io::Error, provided impl From<io::Error> for MyError exists. That one impl is the seam where a library's error taxonomy gets built, and thiserror exists mostly to write those impls for you.

The corollary is that ? in a function returning Result<T, Box<dyn Error>> accepts almost anything, which is convenient in a binary and a poor choice in a library — you have erased the type your callers would need to match on.