Multiple Return Values
Go hands back a result and an error together instead of throwing exceptions, and the caller has to take both.
func parse(s string) (int, error)A Go function can hand back more than one value. The common pair is a result and an error. That pair is how Go reports failure. There is no throwing and catching: a function that can fail says so in its signature, and the caller cannot avoid seeing it.
error is a built-in type. Its value is either nil, which means nothing went wrong, or a value carrying a message about what did. nil is Go's word for "no value". By a convention everyone follows, the error is the last result.
func Atoi(s string) (int, error)strconv.Atoi turns text into a number. It has two results, so the caller writes two variables.
Taking the values
The short form declares and assigns in one line:
n, err := strconv.Atoi("7")The long form is the same work spelled out, declaring first and assigning after:
var n int
var err error
n, err = strconv.Atoi("7")Either way, check if err != nil and deal with it before you use n.
What trips people up
You cannot quietly take one of two values. n := strconv.Atoi("7") fails to compile with assignment mismatch. Write _, the blank identifier, to throw a value away on purpose. And when a call fails, its other results hold their zero values, 0 for a number and "" for a string, so a returned 0 tells you nothing until you have looked at the error.
Example
package main
import (
"fmt"
"strconv"
)
// double hands back two values: the number, and an error.
// The error comes last. nil means nothing went wrong.
func double(text string) (int, error) {
n, err := strconv.Atoi(text) // Atoi is func(string) (int, error)
if err != nil {
return 0, err // 0 is a placeholder the caller must not use
}
return n * 2, nil
}
func main() {
n, err := double("21")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("total:", n)
if _, err := double("twenty"); err != nil {
fmt.Println("error:", err)
}
}
// Output:
// total: 42
// error: strconv.Atoi: parsing "twenty": invalid syntaxWhen to use it
- Turn text typed into a form into a number, and tell the user when it is not one.
- Hand back a loaded record and the reason it could not be loaded in a single call.
- Return a value together with a second value saying whether it was found at all.
More examples
Three results, and building an error
Two results plus an error is normal when a calculation naturally produces a pair, here the whole part and the remainder. Reach for this when splitting the answer across two calls would do the same work twice. Past three or four results, give the values names by returning a struct instead.
package main
import (
"errors"
"fmt"
)
// divide hands back three values. Any number of results is allowed.
// errors.New builds an error from a fixed message.
func divide(a, b int) (int, int, error) {
if b == 0 {
return 0, 0, errors.New("divide by zero")
}
return a / b, a % b, nil
}
func main() {
q, r, err := divide(17, 5)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(q, r)
if _, _, err := divide(1, 0); err != nil {
fmt.Println("error:", err)
}
}
// Output:
// 3 2
// error: divide by zeroShort form, long form, and the blank identifier
The two declarations compile to the same thing; the short form is what you will write, the long form is what you need when the variable has to exist before the call, for example outside an if block. The last pair shows why discarding an error is dangerous: ignored is 0, and 0 is also a perfectly good parsed number.
package main
import (
"fmt"
"strconv"
)
func main() {
// Short form: := declares both variables and assigns in one line.
short, err := strconv.Atoi("7")
if err != nil {
fmt.Println(err)
return
}
// Long form: the same thing, declared with var and then assigned.
var long int
var longErr error
long, longErr = strconv.Atoi("7")
if longErr != nil {
fmt.Println(longErr)
return
}
// _ throws the error away on purpose. ignored is left at 0.
ignored, _ := strconv.Atoi("not a number")
fmt.Println(short, long, ignored)
}
// Output:
// 7 7 0
Discussion