Search code examples
gointerfacetype-assertionmultiple-return-values

Golang: Can you type a returned interface{} in one statement?


Let's say I have this:

type Donut string
type Muffin string

func getPastry () (interface{}, error) {
  // some logic - this is contrived
  var d Donut
  d = "Bavarian"
  return d, nil
}

Is it possible to reduce this to one line:

p, err := getPastry()
thisPastry := p.(Donut)

In other words, something like this, which does not compile:

thisPastry, err := getPastry().(Donut, error)

Not that having two lines of code to get the "generic" and type it is a big deal, but it just feels wasteful and un-simple to me, and that usually means I'm missing something obvious :-)


Solution

  • You can't. Best you can do is write a helper function (and do the type assertion in that):

    func getDonut(p interface{}, err error) (Donut, error) {
        return p.(Donut), err
    }
    

    And then it becomes a one-line:

    d, err := getDonut(getPastry())
    

    Or you may even "incorporate" the getPastry() call in the helper function:

    func getDonutPastry() (Donut, error) {
        p, err := getPastry()
        return p.(Donut), err
    }
    

    And then calling it (an even shorter one-liner):

    d, err := getDonutPastry()
    

    Note:

    Of course if the value returned by getPastry() is not of dynamic type Donut, this will be a runtime panic. To prevent that, you may use the special form of the type assertion:

    v, ok := x.(T)
    

    Which yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.

    Safe versions of the helper functions using the special form could look like this (they return an error rather than panic):

    func getDonut2(p interface{}, err error) (Donut, error) {
        if d, ok := p.(Donut); ok {
            return d, err
        } else {
            return "", errors.New("Not a Donut!")
        }
    }
    
    func getDonutPastry2() (Donut, error) {
        p, err := getPastry()
        if d, ok := p.(Donut); ok {
            return d, err
        } else {
            return "", errors.New("Not a Donut!")
        }
    }
    

    See related questions:

    Return map like 'ok' in Golang on normal functions

    Go: multiple value in single-value context