Python and JavaScript both return exactly one value from a function — returning "more than one thing" means bundling them into a tuple, an object, or an array, and unpacking that bundle on the other end. Go builds returning several values directly into the language itself, and that single feature quietly shapes its entire error-handling philosophy, covered next lesson.
Beginner
3 min read
Multiple return values, without wrapping them in anything
func divide(a, b float64) (float64, error) { if b == 0 { return 0, fmt.Errorf("cannot divide %v by zero", a) } return a / b, nil}result, err := divide(10, 2)
A Go function can return two, three, or more values directly, and the caller receives them as separate, named variables — no tuple to unpack, no object with arbitrarily-named fields to destructure. This looks like a small syntactic convenience, but it's the specific mechanism that makes Go's entire error-handling approach (lesson 6) work naturally: a function returning (result, error) makes "did this fail, and what's the result if not" a first-class, structurally visible part of every function signature, not a convention layered informally on top of exceptions or a single return value.
Named return values: documenting intent directly in the signature
func divide(a, b float64) (result float64, err error) { if b == 0 { err = fmt.Errorf("cannot divide %v by zero", a) return // returns the current values of result and err } result = a / b return}
Go allows naming return values directly in the function signature — result and err are declared (with their zero values) the instant the function starts, and a bare return statement returns whatever those named variables currently hold. This is used sparingly in practice (it can make a long function harder to trace), but it documents what each return position means directly where a reader is already looking, rather than requiring them to trace into the function body to understand what the first vs. second return value actually represents.
The underscore: an explicit way to discard a value you don't need
value, err := someFunction()if err != nil { /* handle it */ }_, err := anotherFunction() // explicitly discard the first return value
Go requires every declared variable to actually be used — an unused variable is a compile error, not just a linter warning. The underscore _ is a special identifier meaning "I'm deliberately not using this value," letting a caller accept only the return values it actually needs from a multi-value function without the compiler complaining about an unused variable.
Variadic functions: a flexible parameter count, still statically typed
func sum(numbers ...int) int { total := 0 for _, n := range numbers { total += n } return total}sum(1, 2, 3) // numbers = []int{1, 2, 3}sum() // numbers = []int{} (empty, not undefined)
A parameter declared with ... accepts any number of arguments of that type, collected into a slice (lesson 10) inside the function — similar in spirit to JavaScript's rest parameters, but still fully statically typed: every argument passed must be an int here, checked at compile time, not merely accepted as whatever type happens to be passed at runtime.