web-dev-qa-db-fra.com

Pourquoi puis-je taper des fonctions d'alias et les utiliser sans transtypage?

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

type MyInt int

Vous ne pouvez pas ensuite passer un MyInt à une fonction qui attend un int, ou inversement:

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 car cela me évite d'avoir à explicitement transtyper newfunc pour taper MyFunc, comme je le ferais dans le premier exemple; cela semble juste incohérent. Je suis sûr qu'il y a une bonne raison pour cela; Quelqu'un peut-il m'éclairer?

La raison pour laquelle je pose cette question est principalement due au fait que je voudrais raccourcir certains de mes types de fonctions plutôt longs de cette façon, mais je veux m'assurer que cela est prévu et acceptable pour le faire :)

81
jsdw

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

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

La distinction que je ne connaissais pas était celle de nommé et de sans nom les types.

Les types nommés sont des types avec un nom, tels que int, int64, float, string, bool. En outre, tout type que vous créez avec "type" est un type nommé.

Sans nom sont les types tels que [chaîne], map [chaîne] chaîne, [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 non nommé, alors 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 est invalide:

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 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 fatigué, je ne le savais pas plus tôt, alors j'espère que cela clarifie un peu le type alouette pour quelqu'un d'autre! Et signifie beaucoup moins de casting que je pensais au début :)

139
jsdw

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

  • Le type nommé est différent du type non nommé .

  • Variable de Le type nommé est assignable à la variable de le type sans nom , et inversement .

  • Variable de type différent nommé n'est pas assignable les uns aux autres.

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
}
13
Mingyu