Ketik irisan antarmuka yang dikonversi

194

Saya ingin tahu mengapa Go tidak secara implisit dikonversi []Tke []interface{}ketika secara implisit dikonversi Tke interface{}. Apakah ada sesuatu yang tidak sepele tentang konversi ini yang saya lewatkan?

Contoh:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build mengeluh

tidak dapat menggunakan (ketik [] string) sebagai tipe [] antarmuka {} dalam argumen fungsi

Dan jika saya mencoba melakukannya secara eksplisit, hal yang sama: b := []interface{}(a)mengeluh

tidak dapat mengonversi string (ketik []) ke antarmuka [] {}

Jadi setiap kali saya perlu melakukan konversi ini (yang tampaknya banyak muncul), saya telah melakukan sesuatu seperti ini:

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

Apakah ada cara yang lebih baik untuk melakukan ini, atau fungsi perpustakaan standar untuk membantu dengan konversi ini? Tampaknya konyol untuk menulis 4 baris kode tambahan setiap kali saya ingin memanggil fungsi yang dapat mengambil daftar misalnya int atau string.

danny
sumber
jadi Anda mendefinisikan "secara implisit konversi [] T ke [] antarmuka {}" berarti "mengalokasikan slice baru dan menyalin semua elemen dari atas". Itu tidak konsisten dengan apa yang "secara implisit mengkonversi T ke antarmuka {}" tidak, yang hanya pandangan dari nilai yang sama dengan tipe statis yang lebih umum; tidak ada yang disalin; dan jika Anda mengetikkannya kembali ke tipe T, Anda masih akan mendapatkan hal yang sama kembali.
Newacct
1
tidak berpikir terlalu detail tentang bagaimana itu akan diterapkan, hanya saja pada tingkat yang lebih tinggi akan lebih mudah untuk dapat dengan mudah mengkonversi seluruh daftar ke tipe lain sebagai solusi untuk tidak memiliki tipe generik.
danny

Jawaban:

215

Di Go, ada aturan umum bahwa sintaksis tidak boleh menyembunyikan operasi yang kompleks / mahal. Konversi a stringke a interface{}dilakukan dalam O (1) waktu. Konversi a []stringke a interface{}juga dilakukan dalam waktu O (1) karena irisan masih satu nilai. Namun, mengubah a []stringmenjadi waktu []interface{}O (n) karena setiap elemen slice harus dikonversi menjadi interface{}.

Satu-satunya pengecualian untuk aturan ini adalah mengonversi string. Saat mengonversi a stringke dan dari a []byteatau a []rune, Go do O (n) berfungsi meskipun konversi adalah "sintaksis".

Tidak ada fungsi perpustakaan standar yang akan melakukan konversi ini untuk Anda. Anda dapat membuat satu dengan pantulan, tetapi itu akan lebih lambat dari opsi tiga baris.

Contoh dengan refleksi:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

Opsi terbaik Anda adalah menggunakan baris kode yang Anda berikan dalam pertanyaan Anda:

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}
Stephen Weinberg
sumber
3
mungkin lebih lambat, tetapi akan bekerja secara umum dengan semua jenis slice
newacct
Seluruh jawaban ini berlaku untuk maps terlalu btw.
RickyA
Ini juga berlaku untuk channels.
Justin Ohms
Terima kasih atas penjelasan yang jelas, jika memungkinkan Anda dapat menambahkan referensi. untuk Converting a []string to an interface{} is also done in O(1) time?
Mayur
56

Hal yang Anda lewatkan adalah bahwa Tdan interface{}yang memiliki nilai Tmemiliki representasi yang berbeda dalam memori sehingga tidak dapat dikonversi secara sepele.

Variabel tipe Thanya nilainya dalam memori. Tidak ada informasi tipe terkait (dalam Go setiap variabel memiliki tipe tunggal yang diketahui pada waktu kompilasi bukan pada saat run time). Itu direpresentasikan dalam memori seperti ini:

  • nilai

Sebuah interface{}memegang variabel jenis Tdirepresentasikan dalam memori seperti ini

  • pointer untuk mengetik T
  • nilai

Jadi kembali ke pertanyaan awal Anda: mengapa pergi tidak secara implisit dikonversi []Tke []interface{}?

Konversi []Tke []interface{}akan melibatkan pembuatan irisan interface {}nilai baru yang merupakan operasi non-sepele karena tata letak dalam memori benar-benar berbeda.

Nick Craig-Wood
sumber
10
Ini informatif dan ditulis dengan baik (+1), tetapi Anda tidak membahas bagian lain dari pertanyaannya: "Apakah ada cara yang lebih baik untuk melakukan ini ..." (-1).
weberc2
13

Berikut adalah penjelasan resmi: https://github.com/golang/go/wiki/InterfaceSlice

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}
Yandry Pozo
sumber
Info bagus tapi itu bukan penjelasan resmi. Ini adalah wiki tempat semua orang dapat mengedit.
Inanc Gumus
Bagaimana saya harus mengonversi []interfaceke tipe yang saya harapkan []map[string]interface{}?
Lewis Chan
8

Coba interface{}saja. Untuk membuang kembali sebagai irisan, coba

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}
dskinner
sumber
3
Ini menimbulkan pertanyaan berikutnya: bagaimana OP kemudian harus beralih baruntuk menafsirkannya sebagai "sepotong jenis apa pun"? Perhatikan bahwa ketiganya menciptakan []interface{}, bukan []stringatau sepotong tipe beton lainnya.
kostix
14
-1: Ini hanya berfungsi jika bilah adalah string [], dalam hal ini, Anda juga dapat menulis:func foo(bar []string) { /* ... */ }
weberc2
2
gunakan saklar jenis, atau gunakan refleksi seperti dalam jawaban yang diterima.
dskinner
OP harus mengetahui tipenya pada saat run time. Menggunakan non slice interface{}akan membuat kompiler tidak mengeluh ketika melewati argumen seperti pada foo(a). Dia bisa menggunakan refleksi atau mengetik switching ( golang.org/doc/effective_go.html#type_switch ) di dalamnya foo.
Cassiohg
2

Konversikan interface{}ke dalam jenis apa pun.

Sintaksis:

result := interface.(datatype)

Contoh:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.
yala ramesh
sumber
2
Apakah Anda yakin akan hal itu? Saya rasa ini tidak valid. Anda akan melihatnya: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao
2

Jika Anda membutuhkan lebih banyak korslet kode, Anda dapat membuat tipe baru untuk helper

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

kemudian

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
RaditzLawliet
sumber