Почему я могу вводить псевдонимы функций и использовать их без преобразования типов?

97

В Go, если вы определяете новый тип, например:

type MyInt int

Затем вы не можете передать a MyIntфункции, ожидающей int, или наоборот:

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

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

Хорошо. Но почему тогда то же самое не относится к функциям? например:

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
}

Теперь я не жалуюсь, потому что это избавляет меня от необходимости явно приводить newfuncтип MyFunc, как в первом примере; это просто кажется непоследовательным. Я уверен, что для этого есть веская причина; кто-нибудь может просветить меня?

Причина, по которой я спрашиваю, в основном потому, что я хотел бы таким образом сократить некоторые из моих довольно длинных типов функций, но я хочу убедиться, что это ожидаемо и приемлемо :)

jsdw
источник
typeгораздо полезнее в Go, чем в Scala. Увы, в Scala есть только псевдонимы типов.
Рик-777
4
Go теперь имеет псевдонимы типов github.com/golang/go/issues/18130
Hut8
может кто-нибудь объяснить второй фрагмент кода? Я действительно не могу получить объявления этих функций
DevX

Ответы:

149

Оказывается, у меня возникло недоразумение по поводу того, как Go работает с типами, и его можно решить, прочитав соответствующую часть спецификации:

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

Соответствующее различие, о котором я не знал, было различие именованных и безымянных типов.

Именованные типы - это типы с именем, например int, int64, float, string, bool. Кроме того, любой тип, который вы создаете с помощью 'type', является именованным типом.

К неназванным типам относятся такие, как [] строка, карта [строка] строка, [4] int. У них нет названия, просто описание, соответствующее тому, как они должны быть структурированы.

Если вы сравниваете два именованных типа, имена должны совпадать, чтобы они могли быть взаимозаменяемыми. Если вы сравниваете именованный и безымянный тип, то до тех пор, пока базовое представление совпадает , все в порядке!

например, учитывая следующие типы:

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

следующее неверно:

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

следующее нормально:

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)

Я немного расстроен, я не знал этого раньше, поэтому я надеюсь, что это немного проясняет типаж для кого-то другого! А значит кастинга гораздо меньше, чем я думал сначала :)

jsdw
источник
1
Вы также можете использовать is := make(MySlice, 0); m := make(MyMap), который в некоторых контекстах более удобочитаем.
R2B2
13

И вопрос, и ответ довольно поучительны. Однако я хотел бы выделить различие, которое неясно в ответе Литнуса.

  • Именованный тип отличается от безымянного типа .

  • Переменная именованного типа присваивается переменной безымянного типа , и наоборот.

  • Переменные другого именованного типа не могут быть присвоены друг другу.

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
}
Мингю
источник