Errors
Why this matters: errors are just a second return value
Go has no try/except. There's a built-in error type — actually just an interface with one method, Error() string — and the convention is that any function that can fail returns one as its last value:
func divide(a, b int) (int, int, error) {
if b == 0 {
return 0, 0, fmt.Errorf("divide by zero: %d / %d", a, b)
}
return a / b, a % b, nil
}nil is what "no error" looks like — the zero value for the error interface. Calling code is expected to check it immediately, before touching the other return values:
q, r, err := divide(10, 0)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(q, r)You will write if err != nil { ... } an enormous number of times in Go code. It looks repetitive the first week and becomes close to invisible after that — your eye learns to skip the pattern and focus on what each branch actually does. The payoff is that every place a call can fail is visible right there in the code, not hidden behind a try block three functions up the stack that might or might not catch the right exception type.
Compare to Python: an exception can be raised deep in a call stack and silently propagate through several functions that never mention it, until something (or nothing) catches it. In Go, if divide can fail, every single caller — all the way up — has to explicitly decide what to do about it, because the error is sitting right there as a value they asked for.
panic and recover — the actual exception mechanism
Go does have something exception-like: panic. But it's reserved for programmer errors and truly unrecoverable situations (an out-of-bounds slice access, a nil pointer dereference), not for expected failure modes like "the file wasn't found" or "the divisor was zero." Those get a returned error. You'll see panic/recover properly in a later lesson — for now, the rule of thumb is: if it's expected, return an error; if it should never happen, let it panic.