Чому я можу вводити функції псевдонімів і використовувати їх без трансляції?


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, так як я мав би зробити в першому прикладі; це просто здається суперечливим. Я впевнений, що для цього є вагома причина; хтось може мене просвітлити?

Причину, про яку я запитую, головним чином тому, що я хотів би скоротити деякі з моїх досить довгих типів функцій таким чином, але я хочу переконатися, що це очікувано та прийнятно робити :)


typeє швидше корисним у Go, ніж Scala. На жаль, Scala має лише псевдоніми типу.
Rick-777,

4
Go now насправді має псевдоніми типу github.com/golang/go/issues/18130
Хата, 8

хтось може пояснити другий фрагмент коду? Я дійсно не можу отримати ці декларації функцій
DevX

Відповіді:


148

Виявляється, це непорозуміння, яке я мав про те, як Go поводився з типами, яке можна вирішити, прочитавши відповідну частину специфікації:

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

Відповідне розрізнення, про яке я не знав, було розмежування іменованих та неназваних типів.

Іменовані типи - це типи з іменем, такі як int, int64, float, string, bool. Крім того, будь-який тип, який ви створюєте за допомогою 'type', є іменованим типом.

Безіменними типами є такі, як [] string, map [string] string, [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)

Я трохи потрошений, що не знав цього раніше, тому сподіваюся, це трохи прояснить тип жайворонків для когось іншого! І означає набагато менше кастингу, ніж я спочатку думав :)


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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.