tidak dapat mengonversi data (ketik antarmuka {}) ke mengetik string: perlu ketikkan jenis

178

Saya cukup baru untuk pergi dan saya bermain dengan paket pemberitahuan ini .

Awalnya saya punya kode yang terlihat seperti ini:

func doit(w http.ResponseWriter, r *http.Request) {
    notify.Post("my_event", "Hello World!")
    fmt.Fprint(w, "+OK")
}

Saya ingin menambahkan baris baru ke Hello World!tetapi tidak dalam fungsi di doitatas, karena itu akan sangat sepele, tetapi handlerkemudian seperti ini di bawah ini:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    fmt.Fprint(w, data + "\n")
}

Setelah go run:

$ go run lp.go 
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)

Setelah sedikit Googling saya menemukan pertanyaan ini di SO .

Kemudian saya memperbarui kode saya ke:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    s:= data.(string) + "\n"
    fmt.Fprint(w, s)
}

Apakah ini yang seharusnya saya lakukan? Kesalahan kompiler saya hilang jadi saya kira itu cukup bagus? Apakah ini efisien? Haruskah Anda melakukannya secara berbeda?

Alfred
sumber

Jawaban:

292

Menurut spesifikasi Go :

Untuk ekspresi x tipe antarmuka dan tipe T, ekspresi primer x. (T) menyatakan bahwa x tidak nol dan bahwa nilai yang disimpan dalam x adalah tipe T.

"Jenis pernyataan" memungkinkan Anda untuk mendeklarasikan nilai antarmuka berisi jenis beton tertentu atau jenis konkretnya memenuhi antarmuka lain.

Dalam contoh Anda, Anda menyatakan data (tipe antarmuka {}) memiliki string tipe konkret. Jika Anda salah, program akan panik saat runtime. Anda tidak perlu khawatir tentang efisiensi, memeriksa hanya membutuhkan membandingkan dua nilai pointer.

Jika Anda tidak yakin apakah itu string atau bukan, Anda dapat menguji menggunakan kedua sintaks pengembalian.

str, ok := data.(string)

Jika data bukan string, ok akan salah. Maka lazim untuk membungkus pernyataan seperti itu menjadi pernyataan if seperti:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}
Stephen Weinberg
sumber
29

Ketikkan Pernyataan

Ini dikenal sebagai type assertiondi golang, dan itu adalah praktik umum.

Berikut ini penjelasan dari tur go :

Pernyataan jenis memberikan akses ke nilai konkret yang mendasari nilai antarmuka.

t := i.(T)

Pernyataan ini menegaskan bahwa nilai antarmuka i memegang tipe beton T dan memberikan nilai T yang mendasarinya ke variabel t.

Jika saya tidak memegang T, pernyataan itu akan memicu kepanikan.

Untuk menguji apakah nilai antarmuka memiliki tipe tertentu, pernyataan tipe dapat mengembalikan dua nilai: nilai dasar dan nilai boolean yang melaporkan apakah pernyataan berhasil.

t, ok := i.(T)

Jika saya memegang T, maka t akan menjadi nilai yang mendasarinya dan ok akan benar.

Jika tidak, ok akan salah dan t akan menjadi nilai nol dari tipe T, dan tidak terjadi kepanikan.

CATATAN: nilai iharus berupa tipe antarmuka .

Perangkap

Sekalipun itipe antarmuka, []ibukan tipe antarmuka. Akibatnya, untuk mengonversi []ike jenis nilainya, kita harus melakukannya secara individual:

// var items []i
for _, item := range items {
    value, ok := item.(T)
    dosomethingWith(value)
}

Performa

Adapun kinerja, itu bisa lebih lambat daripada akses langsung ke nilai aktual seperti yang ditunjukkan dalam jawaban stackoverflow ini .

cizixs
sumber
13
//an easy way:
str := fmt.Sprint(data)
Yuanbo
sumber
21
Tambahkan beberapa penjelasan dengan jawaban untuk bagaimana jawaban ini membantu OP dalam memperbaiki masalah saat ini
ρяσѕρєя K
3

Seperti yang diminta oleh @ ρяσѕρєя penjelasan dapat ditemukan di https://golang.org/pkg/fmt/#Sprint . Penjelasan terkait dapat ditemukan di https://stackoverflow.com/a/44027953/12817546 dan di https://stackoverflow.com/a/42302709/12817546 . Ini jawaban penuh @ Yuanbo.

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}
Tom J
sumber
1
Saya kira Anda bisa menggabungkan kedua jawaban hanya dengan mengedit @ Yuanbo - Anda berdua akan dikreditkan dan menambahkan skor 'kegunaan' Anda masing-masing 😉
Gwyneth Llewelyn