Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 :-)

like image 408
raindog308 Avatar asked Dec 19 '22 14:12

raindog308


2 Answers

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

like image 85
icza Avatar answered Mar 31 '23 10:03

icza


Short answer: It's not possible.

Long answer:
It will be more readable and understandable by more people as there are no long lines chaining together many things.

In your example, I'm assuming you will have to test for an error and then check for a Muffin etc. To make it super clear which types you expect and what to do with them, you can do a type switch:

thisPastry, err := getPastry()
if err != nil { ... }

switch v := thisPastry.(type) {
case Donut:
    fmt.Println(v)
case Muffin:
    fmt.Println(v, "mmm!")
default:
    // some kind of error?
}
like image 31
baloo Avatar answered Mar 31 '23 12:03

baloo