Idiomatic way to do conversion/type assertion on multiple return values in Go

CastingGoReturn Value

Casting Problem Overview


What is the idiomatic way to cast multiple return values in Go?

Can you do it in a single line, or do you need to use temporary variables such as I've done in my example below?

package main

import "fmt"

func oneRet() interface{} {
	return "Hello"
}

func twoRet() (interface{}, error) {
	return "Hejsan", nil
}

func main() {
	// With one return value, you can simply do this
	str1 := oneRet().(string)
	fmt.Println("String 1: " + str1)
	
	// It is not as easy with two return values
	//str2, err := twoRet().(string) // Not possible
	// Do I really have to use a temp variable instead?
	temp, err := twoRet()
	str2 := temp.(string)
	fmt.Println("String 2: " + str2 )
	
	
	if err != nil {
		panic("unreachable")
	}	
}

By the way, is it called casting when it comes to interfaces?

i := interface.(int)

Casting Solutions


Solution 1 - Casting

You can't do it in a single line. Your temporary variable approach is the way to go.

> By the way, is it called casting when it comes to interfaces?

It is actually called a type assertion. A type cast conversion is different:

var a int
var b int64

a = 5
b = int64(a)

Solution 2 - Casting

func silly() (interface{}, error) {
    return "silly", nil
}

v, err := silly()
if err != nil {
    // handle error
}

s, ok := v.(string)
if !ok {
    // the assertion failed.
}

but more likely what you actually want is to use a type switch, like-a-this:

switch t := v.(type) {
case string:
    // t is a string
case int :
    // t is an int
default:
    // t is some other type that we didn't name.
}

Go is really more about correctness than it is about terseness.

Solution 3 - Casting

Or just in a single if:

if v, ok := value.(migrater); ok {
	v.migrate()
}

Go will take care of the cast inside the if clause and let you access the properties of the casted type.

Solution 4 - Casting

template.Must is the standard library's approach for returning only the first return value in one statement. Could be done similarly for your case:

func must(v interface{}, err error) interface{} {
	if err != nil {
		panic(err)
	}
	return v
}

// Usage:
str2 := must(twoRet()).(string)

By using must you basically say that there should never be an error, and if there is, then the program can't (or at least shouldn't) keep operating, and will panic instead.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionANisusView Question on Stackoverflow
Solution 1 - CastingjimtView Answer on Stackoverflow
Solution 2 - CastingjorelliView Answer on Stackoverflow
Solution 3 - CastingTarionView Answer on Stackoverflow
Solution 4 - CastingZippoView Answer on Stackoverflow