Warum kann ich Alias-Funktionen eingeben und ohne Casting verwenden?

97

Wenn Sie in Go einen neuen Typ definieren, z.

type MyInt int

Sie können a dann nicht MyIntan eine Funktion übergeben, die ein int erwartet, oder umgekehrt:

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

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

Fein. Aber warum gilt das nicht auch für Funktionen? z.B:

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
}

Jetzt beschwere ich mich nicht, weil ich dadurch nicht explizit newfuncin Typ umwandeln muss MyFunc, wie ich es im ersten Beispiel tun müsste. es scheint nur inkonsistent. Ich bin sicher, es gibt einen guten Grund dafür; kann mich jemand aufklären?

Der Grund, den ich frage, ist hauptsächlich, dass ich einige meiner ziemlich langen Funktionstypen auf diese Weise kürzen möchte, aber ich möchte sicherstellen, dass dies erwartet und akzeptabel ist :)

jsdw
quelle
typeist in Go eher nützlich als in Scala. Scala hat leider nur Typ-Aliase.
Rick-777
4
Go jetzt hat tatsächlich Typ Aliase github.com/golang/go/issues/18130
Hut8
könnte jemand das zweite Code-Snippet erklären? Ich kann diese Funktionsdeklarationen wirklich nicht bekommen
DevX

Antworten:

148

Es stellt sich heraus, dass dies ein Missverständnis ist, das ich über den Umgang von Go mit Typen hatte, das durch Lesen des relevanten Teils der Spezifikation behoben werden kann:

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

Die relevante Unterscheidung, die mir nicht bekannt war, war die von benannten und unbenannten Typen.

Benannte Typen sind Typen mit einem Namen, z. B. int, int64, float, string, bool. Darüber hinaus ist jeder Typ, den Sie mit 'type' erstellen, ein benannter Typ.

Unbenannte Typen sind solche wie [] string, map [string] string, [4] int. Sie haben keinen Namen, sondern lediglich eine Beschreibung, die ihrer Struktur entspricht.

Wenn Sie zwei benannte Typen vergleichen, müssen die Namen übereinstimmen, damit sie austauschbar sind. Wenn Sie einen benannten und einen unbenannten Typ vergleichen, können Sie loslegen, solange die zugrunde liegende Darstellung übereinstimmt !

zB bei folgenden Typen:

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

Folgendes ist ungültig:

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

Folgendes ist in Ordnung:

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)

Ich bin ein bisschen enttäuscht, das wusste ich nicht früher, also hoffe ich, dass das den Typ Lerche ein wenig für jemand anderen verdeutlicht! Und bedeutet viel weniger Casting als ich zuerst dachte :)

jsdw
quelle
1
Sie können auch verwenden is := make(MySlice, 0); m := make(MyMap), was in einigen Kontexten besser lesbar ist.
R2B2
13

Sowohl die Frage als auch die Antwort sind ziemlich aufschlussreich. Ich möchte jedoch eine Unterscheidung treffen, die in der Antwort von Lytnus nicht klar ist.

  • Der benannte Typ unterscheidet sich vom unbenannten Typ .

  • Eine Variable vom benannten Typ kann einer Variablen vom unbenannten Typ zugewiesen werden , und umgekehrt.

  • Variablen unterschiedlichen benannten Typs können einander nicht zugewiesen werden.

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
quelle