Perché posso digitare funzioni alias e usarle senza eseguire il casting?


97

In Go, se si definisce un nuovo tipo, ad esempio:

type MyInt int

Non puoi quindi passare a MyInta una funzione che si aspetta un int o viceversa:

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

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

Bene. Ma perché allora lo stesso non si applica alle funzioni? per esempio:

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
}

Ora, non mi lamento perché mi evita di dover cast esplicitamente newfuncper digitare MyFunc, come avrei dovuto fare nel primo esempio; sembra solo incoerente. Sono sicuro che ci sia una buona ragione per questo; qualcuno può illuminarmi?

Il motivo per cui lo chiedo è principalmente perché vorrei accorciare alcuni dei miei tipi di funzione piuttosto lunghi in questo modo, ma voglio assicurarmi che sia previsto e accettabile farlo :)


typeè piuttosto più utile in Go che in Scala. Scala ha solo alias di tipo, ahimè.
Rick-777

4
Vai ora ha in realtà alias di tipo github.com/golang/go/issues/18130
Hut8

qualcuno potrebbe spiegare il secondo frammento di codice? Non riesco davvero a ottenere quelle dichiarazioni di funzione
DevX

Risposte:


149

Si è scoperto che questo è un malinteso che ho avuto su come Go ha gestito i tipi, che può essere risolto leggendo la parte pertinente delle specifiche:

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

La distinzione rilevante che non conoscevo era quella di nome e senza nome i tipi.

I tipi denominati sono tipi con un nome, come int, int64, float, string, bool. Inoltre, qualsiasi tipo creato utilizzando "tipo" è un tipo denominato.

I tipi senza nome sono quelli come [] string, map [string] string, [4] int. Non hanno nome, semplicemente una descrizione corrispondente a come devono essere strutturati.

Se si confrontano due tipi con nome, i nomi devono corrispondere affinché siano intercambiabili. Se confronti un tipo con nome e uno senza nome, finché la rappresentazione sottostante corrisponde , sei a posto!

ad esempio dati i seguenti tipi:

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

quanto segue non è valido:

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

va bene quanto segue:

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)

Sono un po 'sventrato non lo sapevo prima, quindi spero che chiarisca un po' il tipo di allodola per qualcun altro! E significa molto meno casting di quanto pensassi all'inizio :)


1
Puoi anche usare is := make(MySlice, 0); m := make(MyMap), che è più leggibile in alcuni contesti.
R2B2

13

Sia la domanda che la risposta sono piuttosto illuminanti. Tuttavia, vorrei sollevare una distinzione che non è chiara nella risposta di lytnus.

  • Il tipo denominato è diverso dal tipo senza nome .

  • La variabile di tipo denominato è assegnabile alla variabile di tipo senza nome , viceversa.

  • Variabili di diverso tipo con nome non sono assegnabili l'una all'altra.

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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.