Thursday, February 28, 2019

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



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


Answer



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


No comments:

Post a Comment

plot explanation - Why did Peaches' mom hang on the tree? - Movies & TV

In the middle of the movie Ice Age: Continental Drift Peaches' mom asked Peaches to go to sleep. Then, she hung on the tree. This parti...