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

54,123

Solution 1

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

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
}
Share:
54,123

Related videos on Youtube

jsdw
Author by

jsdw

Updated on May 11, 2022

Comments

  • jsdw
    jsdw about 2 years

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

    • Rick-777
      Rick-777 over 8 years
      type is rather more useful in Go than Scala. Scala only has type aliases, alas.
    • Hut8
      Hut8 almost 7 years
      Go now actually has type aliases github.com/golang/go/issues/18130
    • DevX
      DevX about 6 years
      could someone explain the second code snippet ? I really cant get those function declarations
  • R2B2
    R2B2 over 5 years
    You can also use is := make(MySlice, 0); m := make(MyMap), which is more readable in some contexts.