Skip to content
Lesson 14 of 14

Step 1 of 7 · Reading · ~4 min

Learn

Pointers and Errors

Multiple Returns and the error Pattern

A Go function can hand back more than one value at a time. By the end of this lesson you can write a function that returns two results, use a named return, and build the (value, error) pair that the whole standard library speaks.

Two values out of one function

List the return types in parentheses after the parameter list, and separate the returned values with commas:

go

Returns are positional: the first value in the return lands in the first variable on the left. Nothing is bundled into a tuple or a struct - splitTime really produces two independent ints, and mins, secs := ... catches both. You can take only one of them with the blank identifier you met in the loops lesson:

go

Naming the returns

You may name the results in the signature. Named results are pre-declared variables, and a bare return sends back whatever they currently hold:

go

Names document which value is which, so (lo, hi int) beats (int, int) in a signature a reader meets cold. Keep naked returns to short functions: in a fifty-line one, nobody can see what return is returning.

The value-and-error pair

The second result is usually an error, and that is the shape the rest of Go is built on. The convention has four parts:

  1. error is the last return type.
  2. On success: the real value, and nil.
  3. On failure: the zero value, and a non-nil error built with errors.New or fmt.Errorf.
  4. The caller checks the error before touching the value.
go

Point 4 is not politeness, it is correctness. The value on the failure path is a real number that means nothing:

go

Guard first, then use the value. A guard is also what keeps a division alive: on int variables, dividing by one that holds 0 does not produce an error, it kills the program with panic: runtime error: integer divide by zero. Checking before dividing is what turns that crash into a value a caller can handle.

fmt.Errorf formats a message with the usual verbs. Its %w verb is special: it wraps the error you pass, so a caller several layers up can still ask errors.Is(err, os.ErrNotExist) about the original. Reach for %w when you add context to an error you received, and errors.New when the message is a fixed string of your own.

Common mistakes

  • Guarding the wrong operand. Testing the left-hand number instead of the divisor makes input 0 and 5 print error: divide by zero, when a zero dividend divides perfectly well and should print result: 0.
  • A non-nil error on the success path. errors.New("") is still non-nil, so the caller takes the failure branch and prints error: with nothing after it - and a placeholder message left in place prints error: todo for every input.
  • Computing before guarding. Divide first and the zero-divisor case never reaches your check: the program panics with integer divide by zero and prints no line at all.
  • Expecting division to round. Go truncates toward zero, so 9 over -2 is -4, not -5 and not -4.5. The hidden tests use negative numbers.

Your exercise

Fill in the body of safeDivide so a zero divisor is reported through its error result instead of crashing the program, and any other divisor yields the quotient with no error. main, the input reading and both output lines are already written; the starter's placeholder fails on every call, so valid divisions stay broken until you fix the success path. The most likely slip is checking the wrong one of the two numbers.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…