Por que posso digitar funções de alias e usá-las sem transmitir?

97

Em Go, se você definir um novo tipo, por exemplo:

type MyInt int

Você não pode então passar um MyIntpara uma função que espera um 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
}

Bem. Mas por que então o mesmo não se aplica às funções? por exemplo:

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
}

Agora, não estou reclamando porque me poupa ter que lançar explicitamente newfuncpara digitar MyFunc, como eu teria que fazer no primeiro exemplo; apenas parece inconsistente. Tenho certeza de que há um bom motivo para isso; alguém pode me esclarecer?

A razão pela qual pergunto é principalmente porque gostaria de encurtar alguns dos meus tipos de função bastante longos desta forma, mas quero ter certeza de que é esperado e aceitável fazer isso :)

jsdw
fonte
typeé muito mais útil no Go do que no Scala. O Scala possui apenas aliases de tipo, infelizmente.
Rick-777 de
4
Go agora tem, na verdade, aliases de tipo github.com/golang/go/issues/18130
Hut8
alguém poderia explicar o segundo trecho de código? Eu realmente não consigo obter essas declarações de função
DevX

Respostas:

148

Acontece que este é um mal-entendido que eu tive sobre como Go lidou com os tipos, que pode ser resolvido lendo a parte relevante da especificação:

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

A distinção relevante que eu desconhecia era a de tipos nomeados e não nomeados .

Tipos nomeados são tipos com um nome, como int, int64, float, string, bool. Além disso, qualquer tipo que você criar usando 'type' é um tipo nomeado.

Tipos sem nome são aqueles como [] string, map [string] string, [4] int. Eles não têm nome, simplesmente uma descrição correspondente a como devem ser estruturados.

Se você comparar dois tipos nomeados, os nomes devem corresponder para que sejam intercambiáveis. Se você comparar um tipo nomeado e um não nomeado, contanto que a representação subjacente corresponda , você está pronto para começar!

por exemplo, dados os seguintes tipos:

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

o seguinte é inválido:

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

o seguinte está bom:

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)

Estou um pouco arrasado por não saber disso antes, então espero que isso esclareça um pouco o tipo de cotovia para outra pessoa! E significa muito menos elenco do que eu pensava inicialmente :)

jsdw
fonte
1
Você também pode usar is := make(MySlice, 0); m := make(MyMap), que é mais legível em alguns contextos.
R2B2
13

Tanto a pergunta quanto a resposta são bastante esclarecedoras. No entanto, gostaria de apresentar uma distinção que não está clara na resposta de Lytnus.

  • Tipo com nome é diferente do tipo sem nome .

  • Variável do tipo nomeado é atribuído a variável do Tipo Sem nome , vice-versa.

  • Variáveis ​​de diferentes tipos nomeados não podem ser atribuídas umas às outras.

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
fonte