goreturn-valuemultiple-return-values

Return map like 'ok' in Golang on normal functions


In Go, the following works (note one use of the map has one return, the other has two returns)

package main

import "fmt"

var someMap = map[string]string { "some key": "hello" }

func main() {
    if value, ok := someMap["some key"]; ok {
        fmt.Println(value)
    }

    value := someMap["some key"]
    fmt.Println(value)
}

However, I have no idea how to do this same thing with my own function. Is it possible to have similar behavior with an optional return like map?

For example:

package main

import "fmt"

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

func main() {
    if value, ok := Hello(); ok {
        fmt.Println(value)
    }

    value := Hello()
    fmt.Println(value)
}

Wont compile (due to the error multiple-value Hello() in single-value context) ... is there a way to make this syntax work for the function Hello()?


Solution

  • map is different because it is a built-in type and not a function. The 2 forms of accessing an element of a map is specified by the Go Language Specification: Index Expressions and backed by the compiler.

    With functions you can't do this. If a function has 2 return values, you have to "expect" both of them or none at all.

    However you are allowed to assign any of the return values to the Blank identifier:

    s, b := Hello()    // Storing both of the return values
    
    s2, _ := Hello()   // Storing only the first
    
    _, b3 := Hello()   // Storing only the second
    

    You can also choose not to store any of the return values:

    Hello()            // Just executing it, but storing none of the return values
    

    Note: you could also assign both of the return values to the blank identifier, although it has no use (other than validating that it has exactly 2 return values):

    _, _ = Hello()     // Storing none of the return values; note the = instead of :=
    

    You can also try these on the Go Playground.

    Helper function

    If you use it many times and you don't want to use the blank identifier, create a helper function which discards the 2nd return value:

    func Hello2() string {
        s, _ := Hello()
        return s
    }
    

    And now you can do:

    value := Hello2()
    fmt.Println(value)
    

    Go 1.18 generics update: Go 1.18 adds generics support, it is now possible to write a generic First() function which discards the second (or any further) return values:

    func First[T any](first T, _ ...any) T {
        return first
    }
    

    This is available in github.com/icza/gog, as gog.First() (disclosure: I'm the author).

    Using it:

    value := First(Hello())
    fmt.Println(value)