Why can I type alias functions and use them without casting?

TypesCastingGo

Types Problem Overview


In Go, if you define a new type e.g.:

type MyInt int

You can't then pass a MyInt to a function expecting an int, or vice versa:

func test(i MyInt) {
    //do something with i
}

func main() {
    anInt := 0
    test(anInt) //doesn't work, int is not of type MyInt
}

Fine. But why is it then that the same does not apply to functions? e.g.:

type MyFunc func(i int)
func (m MyFunc) Run(i int) {
    m(i)
}

func run(f MyFunc, i int) {
    f.Run(i)
}

func main() {
    var newfunc func(int) //explicit declaration
    newfunc = func(i int) {
        fmt.Println(i)
    }
    run(newfunc, 10) //works just fine, even though types seem to differ
}

Now, I'm not complaining because it saves me having to explicitly cast newfunc to type MyFunc, as I would have to do in the first example; it just seems inconsistent. I'm sure there is a good reason for it; can anyone enlighten me?

The reason I ask is mainly because I would like to shorten some of my rather long function types in this way, but I want to make sure it's expected and acceptable to do this :)

Types Solutions


Solution 1 - Types

Turns out, this is a misunderstanding that I had about how Go dealt with types, which can be resolved by reading the relevant part of the spec:

http://golang.org/ref/spec#Type_identity

The relevant distinction that I was unaware of was that of named and unnamed types.

Named types are types with a name, such as int, int64, float, string, bool. In addition, any type you create using 'type' is a named type.

Unnamed types are those such as []string, map[string]string, [4]int. They have no name, simply a description corresponding to how they are to be structured.

If you compare two named types, the names must match in order for them to be interchangeable. If you compare a named and an unnamed type, then as long as the underlying representation matches, you're good to go!

e.g. given the following types:

type MyInt int
type MyMap map[int]int
type MySlice []int
type MyFunc func(int)

the following is invalid:

var i int = 2
var i2 MyInt = 4
i = i2 //both named (int and MyInt) and names don't match, so invalid

the following is fine:

is := make([]int)
m := make(map[int]int)
f := func(i int){}

//OK: comparing named and unnamed type, and underlying representation
//is the same:
func doSlice(input MySlice){...}
doSlice(is)

func doMap(input MyMap){...}
doMap(m)

func doFunc(input MyFunc){...}
doFunc(f)

I'm a bit gutted I didn't know that sooner, so I hope that clarifies the type lark a little for someone else! And means much less casting than I at first thought :)

Solution 2 - Types

Both the question and answer are pretty enlightening. However, I'd like to bring up a distinction which is not clear in lytnus's answer.

  • Named Type is different from Unnamed Type.

  • Variable of Named Type is assignable to variable of Unnamed Type, vice versa.

  • Variable of different Named Type is not assignable to each other.

http://play.golang.org/p/uaYHEnofT9

import (
	"fmt"
	"reflect"
)

type T1 []string
type T2 []string

func main() {
	foo0 := []string{}
	foo1 := T1{}
	foo2 := T2{}
	fmt.Println(reflect.TypeOf(foo0))
	fmt.Println(reflect.TypeOf(foo1))
	fmt.Println(reflect.TypeOf(foo2))
	
	// Output:
	// []string
	// main.T1
	// main.T2

	// foo0 can be assigned to foo1, vice versa
	foo1 = foo0
	foo0 = foo1
	
	// foo2 cannot be assigned to foo1
	// prog.go:28: cannot use foo2 (type T2) as type T1 in assignment
	// foo1 = foo2
}

Solution 3 - Types

If the ultimate(as type of a type maybe a case)underlying type is a primitive type GO will not allow direct assignment of variable of one type to the another type directly and vice versa. However the value can be casted to the destination type and used.

package main
import (
	"fmt"
)

type T int

type U T

type V U

type UU U

func main() {

	var s int 
    var t T   
    var u U
    var uu UU

	s = 9
	t = 10
	u = 11
	uu = 111

	fmt.Println(s)
	fmt.Println(t)
	fmt.Println(u)
	fmt.Println(uu)

	fmt.Println("========")

	//s = t
	//u = s
	//v = s

	u = (U)(uu)

	//fmt.Println(s)
	//fmt.Println(u)
	fmt.Println(u)

}

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
QuestionjsdwView Question on Stackoverflow
Solution 1 - TypesjsdwView Answer on Stackoverflow
Solution 2 - TypesMingyuView Answer on Stackoverflow
Solution 3 - TypesSnehil SinghView Answer on Stackoverflow