Go if err != nil versus Rust's ? operator
judgment on error-handling ergonomics.
Go's explicit checks are verbose but make every error site visible; Rust's ? propagates concisely while still forcing the error into the type, reducing boilerplate.
What's really being asked
It probes whether you understand the ergonomics and safety trade-offs of explicit error checks versus concise propagation, without mischaracterizing either mechanism.
The full answer
Go's idiom is to assign (val, err) and immediately write if err != nil { return ..., err }. This is explicit and uniform: every place an error can occur is visible in the code, which aids auditing, but it is verbose and can dominate a function with boilerplate. Crucially, the compiler does not force the check; you can ignore err via the blank identifier, so unhandled errors are possible and rely on linters and review. Rust's ? operator, written after a Result-returning expression, evaluates to the inner value on Ok and on Err returns early from the enclosing function after applying a From conversion to the error type. This keeps the happy path readable and removes boilerplate, while the type system still requires you to handle the Result; to deliberately ignore one you must write unwrap, expect, or let _ =, making the choice explicit and greppable.
Verbosity and readability
Go trades concision for visibility and simplicity. Rust trades a small amount of magic in ? for concision plus stronger guarantees. Neither uses exceptions; ? is ordinary early return, not unwinding.
The mistakes people make
Calling ? a hidden exception or saying it can panic (it returns, it does not panic). Saying Go forces you to handle errors. Claiming Rust hides where errors occur; ? marks each propagation point with a visible symbol.
What usually comes next
How does ? interact with the From trait and error conversion? What return types allow ? in main? How do anyhow and thiserror change the ergonomics?
A concrete example
Reading a file then parsing it: in Go you write two separate if err != nil blocks between the open, read, and parse calls. In Rust the same logic is let data = std::fs::read_to_string(path)?; let n: i32 = data.trim().parse()?; where each ? both propagates the error and converts it, and the function still cannot pretend the errors do not exist.
Interview question
What is true about Rust's ? operator compared with Go's explicit if err != nil checks?
- a.? concisely propagates errors as an early return while the type still forces handling; Go's check can be silently skippedCorrect
- b.? hides the location of error propagation entirely
- c.Go's compiler forces every error to be handled, unlike Rust
- d.? throws an exception that unwinds the stack
Why? this is the answer
? is sugar for an early return on Err (with From conversion), keeping handling type-enforced, while Go lets you discard err with the blank identifier. ? is not an exception and marks each propagation point visibly.
Just read this? Test yourself on what you have been reading.
- #go
- #rust
- #error-handling
- #question-mark-operator
- #ergonomics
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on go — each one lists the topics its interview covers.
See open roles