Last Updated: May 22, 2026
Go lets you give names to the values a function returns, right in the signature. The names act like normal variables inside the function body, pre-declared and initialized to their zero values. This lesson covers the syntax, the naked return statement that pairs with it, and the cases where named returns help versus the cases where they quietly cause bugs.
A regular multi-return function lists the types in parentheses after the parameters. To name the returns, write name type for each one, just like you would for parameters.
Two things are happening here that aren't obvious from the syntax alone.
First, total and err are declared the moment the function is entered. You don't write var total float64 or var err error inside the body. They already exist, pre-initialized to their zero values (0.0 for float64, nil for error). You can read them, assign to them, or pass them around like any other local variable.
Second, the names become part of the function's public signature. If you generate documentation, the names show up next to each return type. That gives callers a hint about what each value means without needing to read the function body or guess from the call site.
Compare these two signatures:
Both are legal. The second one tells you at a glance that the first return is the cost, the second is the number of days, and the third is an error. The first version forces you to look at the body or hope the variable names at the call site are good.
Once the return values have names, you can write a bare return statement with no expressions after it. Go calls this a "naked return", and it returns the current values of the named return variables.
The return at the bottom has no expressions. Go reads the current values of cost and estimatedDays, and those are what the caller receives. It's equivalent to writing return cost, estimatedDays, just shorter.
A naked return is purely syntactic sugar. The compiler emits the same code for return and return cost, estimatedDays as long as the named variables hold the values you want. You can mix the styles, too. A function with named returns can still use explicit returns when that reads better:
The error path returns explicit values because returning 0 and a specific error is clearer than mutating total and err first. The happy path uses a naked return because the named variables already hold the right values.
Cost: Naked returns are zero overhead at runtime. The compiler generates the same machine code as the explicit form. The cost is to readers, not to the CPU.
Named returns are useful in a few specific situations.
Small functions where the names document the meaning. A return signature like (cost float64, estimatedDays int, err error) reads better than (float64, int, error). For functions short enough to fit on one screen, this is the main reason to use them.
Functions where `defer` modifies a return value before it leaves. This is the one place named returns are not just style, they're load-bearing. A deferred call can read and write a named return after the body finishes but before the caller sees it. Common pattern: a defer that wraps an error with extra context.
The deferred function reads err, sees that it's non-nil, and replaces it with a wrapped version that includes the order ID. Without the named return err, the deferred function would have no way to reach the value the function is about to return. Named returns and defer work together when you need this kind of "post-process the return" pattern.
Functions with many returns of the same type. When a function returns several ints or several float64s, named returns prevent the caller from mixing them up at the call site.
A caller reading cartTotals(prices) from the signature alone now knows which float64 is which.
Named returns are not free. They can make code worse in a few ways, which is why most Go style guides treat them as a tool to use with care rather than a default.
Long functions become harder to track. In a 60-line function, a naked return at the bottom forces the reader to scroll back and figure out the latest value of each named variable. Explicit returns put the result right there. The longer the function, the worse the naked return reads.
Names invite shadowing bugs. Because the named return is just a normal local variable, a := inside the function can accidentally declare a new variable with the same name in an inner scope. The named return then keeps its zero value, and a naked return silently returns zero.
The lookup succeeds, but the function returns $0.00. Inside the if block, price := p declares a brand new local variable named price that shadows the named return. The new variable goes out of scope at the closing brace, and the named return price was never touched. The naked return then returns the zero value, 0.0, with no error.
The fix is to drop the := and just assign:
That writes to the named return instead of declaring a new variable. go vet will warn about some shadowing cases, and tools like staticcheck catch more of them, but the language itself happily compiles the buggy version.
Mixed styles in one function read worse than either style alone. If you name the returns, lean on the names or skip them entirely. A function that names its returns and then writes return total, err for every exit gets the worst of both: the visual weight of named returns and none of the brevity.
A reasonable default for most Go code: use named returns when you need defer to read or modify a return value, or when names genuinely improve the signature documentation for a short function. For long functions, or any function with branchy logic and multiple early returns, prefer unnamed returns and explicit return value1, value2 statements at every exit. The Go standard library follows roughly this convention, visible in the io, bufio, and net/http packages.