¿Por qué puedo escribir funciones de alias y usarlas sin conversión?

97

En Go, si define un nuevo tipo, por ejemplo:

type MyInt int

Entonces no puede pasar un MyInta una función esperando 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
}

Multa. Pero, ¿por qué entonces no se aplica lo mismo a las funciones? p.ej:

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
}

Ahora, no me quejo porque me ahorra tener que lanzar explícitamente newfuncpara escribir MyFunc, como tendría que hacer en el primer ejemplo; simplemente parece inconsistente. Estoy seguro de que hay una buena razón para ello; ¿Alguien puede iluminarme?

La razón por la que pregunto es principalmente porque me gustaría acortar algunos de mis tipos de funciones bastante largos de esta manera, pero quiero asegurarme de que sea esperado y aceptable para hacer esto :)

jsdw
fuente
typees bastante más útil en Go que en Scala. Scala solo tiene alias de tipo, por desgracia.
Rick-777
4
Go ahora tiene alias de tipo github.com/golang/go/issues/18130
Hut8
¿Alguien podría explicar el segundo fragmento de código? Realmente no puedo obtener esas declaraciones de funciones
DevX

Respuestas:

148

Resulta que este es un malentendido que tuve sobre cómo Go manejó los tipos, que se puede resolver leyendo la parte relevante de la especificación:

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

La distinción relevante que no era consciente de que era de nombre y sin nombre tipos.

Nombrados tipos son tipos con un nombre, como int, Int64, float, string, int. Además, cualquier tipo que cree utilizando 'tipo' es un tipo con nombre.

Los tipos sin nombre son aquellos como [] string, map [string] string, [4] int. No tienen nombre, simplemente una descripción correspondiente a cómo se van a estructurar.

Si compara dos tipos con nombre, los nombres deben coincidir para que sean intercambiables. Si compara un tipo con nombre y uno sin nombre, siempre que la representación subyacente coincida , ¡está listo para comenzar!

por ejemplo, dados los siguientes tipos:

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

lo siguiente no es válido:

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

lo siguiente 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)

Estoy un poco destrozado, no lo sabía antes, ¡así que espero que eso aclare un poco el tipo de alondra para alguien más! Y significa mucho menos casting de lo que pensé al principio :)

jsdw
fuente
1
También puede usar is := make(MySlice, 0); m := make(MyMap), que es más legible en algunos contextos.
R2B2
13

Tanto la pregunta como la respuesta son bastante esclarecedoras. Sin embargo, me gustaría plantear una distinción que no está clara en la respuesta de lytnus.

  • El tipo con nombre es diferente del tipo sin nombre .

  • La variable de tipo con nombre se puede asignar a la variable de tipo sin nombre , y viceversa.

  • Las variables de diferentes tipos con nombre no se pueden asignar entre sí.

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
fuente