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 THIS TESTS It probes whether you understand the ergonomics and safety trade-offs of explicit error checks versus concise propagation, without mischaracterizing either mechanism.
A GOOD ANSWER COVERS 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.
COMMON WRONG ANSWERS 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.
LIKELY FOLLOW-UPS How does ? interact with the From trait and error conversion? What return types allow ? in main? How do anyhow and thiserror change the ergonomics?
ONE 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.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.