Mengapa saya dapat mengetik fungsi alias dan menggunakannya tanpa transmisi?

97

Di Go, jika Anda mendefinisikan tipe baru misalnya:

type MyInt int

Anda tidak dapat meneruskan a MyIntke fungsi yang mengharapkan int, atau sebaliknya:

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

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

Baik. Tetapi mengapa hal yang sama tidak berlaku untuk fungsi? misalnya:

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
}

Sekarang, saya tidak mengeluh karena saya tidak perlu melakukan cast newfuncuntuk mengetik MyFunc, seperti yang harus saya lakukan di contoh pertama; sepertinya tidak konsisten. Saya yakin ada alasan bagus untuk itu; Adakah yang bisa mencerahkan saya?

Alasan saya bertanya terutama karena saya ingin mempersingkat beberapa jenis fungsi saya yang agak panjang dengan cara ini, tetapi saya ingin memastikan hal ini diharapkan dan dapat diterima untuk dilakukan :)

jsdw
sumber
typelebih berguna di Go daripada Scala. Scala hanya memiliki alias tipe, sayangnya.
Rick-777
4
Go sekarang sebenarnya memiliki alias tipe github.com/golang/go/issues/18130
Hut8
dapatkah seseorang menjelaskan potongan kode kedua? Saya benar-benar tidak bisa mendapatkan deklarasi fungsi tersebut
DevX

Jawaban:

149

Ternyata, ini adalah kesalahpahaman yang saya miliki tentang bagaimana Go menangani tipe, yang dapat diselesaikan dengan membaca bagian spesifikasi yang relevan:

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

Perbedaan relevan yang tidak saya sadari adalah perbedaan tipe bernama dan tidak disebutkan .

Tipe yang diberi nama adalah tipe dengan nama, seperti int, int64, float, string, bool. Selain itu, tipe apa pun yang Anda buat menggunakan 'tipe' adalah tipe bernama.

Jenis yang tidak disebutkan namanya adalah seperti [] string, peta [string] string, [4] int. Mereka tidak memiliki nama, hanya deskripsi yang sesuai dengan bagaimana mereka akan disusun.

Jika Anda membandingkan dua jenis bernama, nama tersebut harus cocok agar dapat dipertukarkan. Jika Anda membandingkan tipe bernama dan tipe tanpa nama, selama representasi yang mendasarinya cocok , Anda siap melakukannya!

misalnya diberi tipe berikut:

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

berikut ini tidak valid:

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

berikut ini baik-baik saja:

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)

Saya agak kecewa karena saya tidak mengetahuinya lebih awal, jadi saya harap hal itu menjelaskan jenis burung itu sedikit untuk orang lain! Dan berarti casting jauh lebih sedikit daripada yang saya pikirkan sebelumnya :)

jsdw
sumber
1
Anda juga dapat menggunakan is := make(MySlice, 0); m := make(MyMap), yang lebih mudah dibaca dalam beberapa konteks.
R2B2
13

Baik pertanyaan maupun jawabannya cukup mencerahkan. Namun, saya ingin mengemukakan perbedaan yang tidak jelas dalam jawaban lytnus.

  • Jenis Bernama berbeda dari Jenis Tanpa Nama .

  • Variabel Jenis Bernama dapat dialihkan ke variabel Jenis Tanpa Nama , sebaliknya.

  • Variabel dari Jenis Bernama yang berbeda tidak dapat ditetapkan satu sama lain.

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
sumber