Pourquoi puis-je saisir des fonctions d'alias et les utiliser sans conversion?

97

Dans Go, si vous définissez un nouveau type, par exemple:

type MyInt int

Vous ne pouvez pas alors passer a MyIntà une fonction qui attend un int, ou 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
}

Bien. Mais pourquoi est-ce alors que la même chose ne s'applique pas aux fonctions? par exemple:

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
}

Maintenant, je ne me plains pas parce que cela m'évite d'avoir à faire un cast explicitement en newfunctype MyFunc, comme je devrais le faire dans le premier exemple; cela semble simplement incohérent. Je suis sûr qu'il y a une bonne raison à cela; quelqu'un peut-il m'éclairer?

La raison pour laquelle je pose la question est principalement parce que je voudrais raccourcir certains de mes types de fonctions plutôt longs de cette manière, mais je veux m'assurer que cela est attendu et acceptable :)

jsdw
la source
typeest plutôt plus utile dans Go que Scala. Scala n'a que des alias de type, hélas.
Rick-777
4
Go a maintenant en fait des alias de type github.com/golang/go/issues/18130
Hut8
quelqu'un pourrait-il expliquer le deuxième extrait de code? Je ne peux vraiment pas obtenir ces déclarations de fonction
DevX

Réponses:

148

Il s'avère que c'est un malentendu que j'avais sur la façon dont Go traitait les types, qui peut être résolu en lisant la partie pertinente de la spécification:

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

La distinction pertinente que j'ignorais était celle des types nommés et non nommés .

Les types nommés sont des types avec un nom, tel que int, int64, float, string, bool. De plus, tout type que vous créez à l'aide de 'type' est un type nommé.

Les types sans nom sont ceux tels que [] string, map [string] string, [4] int. Ils n'ont pas de nom, simplement une description correspondant à la manière dont ils doivent être structurés.

Si vous comparez deux types nommés, les noms doivent correspondre pour qu'ils soient interchangeables. Si vous comparez un type nommé et un type sans nom, tant que la représentation sous-jacente correspond , vous êtes prêt à partir!

par exemple étant donné les types suivants:

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

ce qui suit n'est pas valide:

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

ce qui suit est très bien:

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)

Je suis un peu vidé je ne savais pas ça plus tôt, alors j'espère que ça clarifie un peu le type alouette pour quelqu'un d'autre! Et cela signifie beaucoup moins de casting que je ne le pensais au début :)

jsdw
la source
1
Vous pouvez également utiliser is := make(MySlice, 0); m := make(MyMap), qui est plus lisible dans certains contextes.
R2B2
13

La question et la réponse sont toutes deux assez éclairantes. Cependant, je voudrais faire une distinction qui n'est pas claire dans la réponse de Lytnus.

  • Le type nommé est différent du type sans nom .

  • La variable de type nommé peut être affectée à la variable de type sans nom , vice versa.

  • Une variable de type nommé différent ne peut pas être affectée l'une à l'autre.

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
}
Mingyu
la source