[go] Multiple values in single-value context

Due to error handling in Go, I often end up with multiple values functions. So far, the way I have managed this has been very messy and I am looking for best practices to write cleaner code.

Let's say I have the following function:

type Item struct {
   Value int
   Name string
}

func Get(value int) (Item, error) {
  // some code

  return item, nil
}

How can I assign a new variable to item.Value elegantly. Before introducing the error handling, my function just returned item and I could simply do this:

val := Get(1).Value

Now I do this:

item, _ := Get(1)
val := item.Value

Isn't there a way to access directly the first returned variable?

The answer is


How about this way?

package main

import (
    "fmt"
    "errors"
)

type Item struct {
    Value int
    Name string
}

var items []Item = []Item{{Value:0, Name:"zero"}, 
                        {Value:1, Name:"one"}, 
                        {Value:2, Name:"two"}}

func main() {
    var err error
    v := Get(3, &err).Value
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(v)

}

func Get(value int, err *error) Item {
    if value > (len(items) - 1) {
        *err = errors.New("error")
        return Item{}
    } else {
        return items[value]
    }
}

No, you cannot directly access the first value.

I suppose a hack for this would be to return an array of values instead of "item" and "err", and then just do item, _ := Get(1)[0] but I would not recommend this.


No, but that is a good thing since you should always handle your errors.

There are techniques that you can employ to defer error handling, see Errors are values by Rob Pike.

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

In this example from the blog post he illustrates how you could create an errWriter type that defers error handling till you are done calling write.


Here's a generic helper function with assumption checking:

func assumeNoError(value interface{}, err error) interface{} {
    if err != nil {
        panic("error encountered when none assumed:" + err.Error())
    }
    return value
}

Since this returns as an interface{}, you'll generally need to cast it back to your function's return type.

For example, the OP's example called Get(1), which returns (Item, error).

item := assumeNoError(Get(1)).(Item)

The trick that makes this possible: Multi-values returned from one function call can be passed in as multi-variable arguments to another function.

As a special case, if the return values of a function or method g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(parameters_of_g)) will invoke f after binding the return values of g to the parameters of f in order.


This answer borrows heavily from existing answers, but none had provided a simple, generic solution of this form.


Yes, there is.

Surprising, huh? You can get a specific value from a multiple return using a simple mute function:

package main

import "fmt"
import "strings"

func µ(a ...interface{}) []interface{} {
    return a
}

type A struct {
    B string
    C func()(string)
}

func main() {
    a := A {
        B:strings.TrimSpace(µ(E())[1].(string)),
        C:µ(G())[0].(func()(string)),
    }

    fmt.Printf ("%s says %s\n", a.B, a.C())
}

func E() (bool, string) {
    return false, "F"
}

func G() (func()(string), bool) {
    return func() string { return "Hello" }, true
}

https://play.golang.org/p/IwqmoKwVm-

Notice how you select the value number just like you would from a slice/array and then the type to get the actual value.

You can read more about the science behind that from this article. Credits to the author.


Examples related to go

Has been blocked by CORS policy: Response to preflight request doesn’t pass access control check Go test string contains substring Golang read request body How to uninstall Golang? Decode JSON with unknown structure Access HTTP response as string in Go How to search for an element in a golang slice How to delete an element from a Slice in Golang How to set default values in Go structs MINGW64 "make build" error: "bash: make: command not found"

Examples related to error-handling

must declare a named package eclipse because this compilation unit is associated to the named module Error:Failed to open zip file. Gradle's dependency cache may be corrupt What does 'index 0 is out of bounds for axis 0 with size 0' mean? What's the source of Error: getaddrinfo EAI_AGAIN? Error handling with try and catch in Laravel What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean? Raise error in a Bash script Javascript Uncaught TypeError: Cannot read property '0' of undefined Multiple values in single-value context IndexError: too many indices for array

Examples related to return-value

Multiple values in single-value context MySQL stored procedure return value Best way to return a value from a python script How to return a value from try, catch, and finally? Return value in a Bash function How to return a string from a C++ function? Efficient way to return a std::vector in c++ Return value from a VBScript function store return value of a Python script in a bash script How can I return two values from a function in Python?

Examples related to multiple-return-values

Multiple values in single-value context