AlgoMaster Logo

Multiple Return Values

Last Updated: May 22, 2026

High Priority
4 min read

Most languages let a function hand back one thing. Go lets it hand back several, and that one design choice shapes how Go programs handle errors and ask questions about data. This lesson covers the syntax, the (value, error) idiom that shows up everywhere, and the comma-ok form for map lookups.

Why Multiple Returns Exist

A function that parses a price from a checkout form has two things to tell the caller: the number it produced and whether the input was valid. Other languages solve this awkwardly with exceptions, tuples, or out-parameter pointers.

Go declares more than one return type, and the caller receives them on the same line.

The type list (string, string) says the function gives back two strings. The return provides both, comma-separated. The caller binds them to two variables in order. One feature replaces exceptions, tuples, and out-params at once.

Declaring and Returning Multiple Values

The return type list goes in parentheses. With one value the parentheses are optional. With two or more they're required.

The order in the type list is the order in the return and the order on the caller's left. Get them wrong and either the compiler catches a type mismatch or, worse, same-typed values swap without any compiler warning. The standard library convention is primary value first, error last.

The (value, error) Idiom

Most Go functions that can fail return (T, error). The first slot holds the result, the second is an error that's either nil (success) or a non-nil description of what went wrong. The standard library uses this shape almost everywhere: os.Open, json.Unmarshal, http.Get, and strconv.Atoi.

strconv.Atoi converts a string like "42" into the integer 42. On bad input, it returns the int's zero value and a non-nil error. Imagine the store's web form sends the chosen quantity as a string, and you need to convert it before adding the item to the cart.

Two assignments, one call, two return values. The if err != nil check is the standard pattern in Go programs.

Now with a bad string:

When err is non-nil, don't trust the first return value. strconv.Atoi happens to return 0, but other functions may leave the first value undefined.

Writing your own version is straightforward. Parse a price from input and reject negatives:

Two guard clauses each return (0, err), and the happy path returns (price, nil). The function returns zero on failure as a defensive default. The caller still must check err first.

Receiving Multiple Returns

The caller has to do something with every return value. There's no implicit "first value wins" rule. The most common pattern is multiple assignment with :=:

To ignore a value, use the blank identifier _. It tells the compiler "I see this return slot, and I'm choosing not to bind it."

You cannot silently drop returns. Writing quantity := strconv.Atoi("5") is a compile error: multiple-value strconv.Atoi() in single-value context. Every slot must be claimed by a variable or by _.

Ignoring errors in production code is almost always a bug. The linter errcheck catches _ = someCall() patterns where the error gets discarded.

If every name on the left already exists, use =. With :=, at least one name must be new; existing ones get reassigned.

Reusing err across multiple := statements is idiomatic. As long as one name on the left is new (here total), the existing err is reassigned in place.

The Comma-Ok Pattern with Maps

Multiple returns aren't only a function feature. A few language operations also produce two values, and the most common is map lookup.

When you read a key from a map, Go can hand back the value and a boolean saying whether the key was there. This is the "comma-ok" pattern.

The single-value form count := stock["Headphones"] also gives 0, but with no way to tell "missing" from "present with zero stock". Comma-ok removes that ambiguity. Use it whenever the zero value of the map's value type is a legal entry, which for int is essentially always.

This isn't a different feature from function returns. The same multi-assignment syntax applies, and you'll see the same shape with channel receives (v, ok := <-ch) and type assertions (s, ok := i.(string)).

Returning Three or More Values

Nothing stops you from returning three or more values. The syntax extends in the obvious way.

Three returns is the comfortable upper bound. Past that, callers have to remember which slot is which, and a swap between two float64 returns hides bugs the compiler won't catch.

Four or more return values is a signal to group them into a struct.

Now the function returns the struct and an error. The caller writes summary.Total instead of remembering that "total" is the fourth slot. The signature (CartSummary, error) reads better than (int, float64, float64, float64, error). The standard library does the same: os.Stat returns (FileInfo, error), not a long list of bytes, mode bits, and timestamps.

The rule of thumb: two or three returns with distinct types and roles is fine. Same-typed returns or four-plus values, use a struct.