Why does Go treat errors as values instead of using exceptions? What are the trade-offs?
In Go, error is an ordinary interface (type error interface{ Error() string }) returned as the last result. Failure paths are explicit in the signature and the control flow: no hidden jumps, no unknown set of exceptions escaping from a call. Because errors are values, you can store them, compare them, wrap them, aggregate them, send them over channels and program against them (for example, bufio.Scanner keeps the error and reports it once through Err()).
Pros: the reader sees where things can fail, the cost is predictable (no stack unwinding), and handling happens close to the cause. Cons: more if err != nil boilerplate, errors are easy to ignore silently (use errcheck, staticcheck or golangci-lint; plain go vet does not report unchecked errors), and errors carry no stack trace by default.
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("load config %q: %w", path, err)
}
defer f.Close()
What the interviewer wants to hear: that panic is not Go's exception system. It is for unrecoverable or programmer errors. Expected failures (I/O, validation, not-found) are values.
More on Error Handling & panics
- Q295What does this print, and why?
errors.New("x") == errors.New("x") - Q296What is the difference between
%wand%vinfmt.Errorf? - Q297How does
errors.Iswork internally, and how can a custom type change its matching? - Q298Explain
errors.As. Why must the target be a pointer, and what happens if you get it wrong? - Q299What is
errors.Join? How do multi-errors interact witherrors.Is,errors.Asanderrors.Unwrap? - Q300Compare sentinel errors, typed errors and opaque errors. When would you use each?