Kesalahan waktu kompilasi ini muncul ketika Anda mencoba untuk menetapkan atau meneruskan (atau mengkonversi) tipe konkret ke tipe antarmuka; dan tipe itu sendiri tidak mengimplementasikan antarmuka, hanya pointer ke tipe .
Mari kita lihat sebuah contoh:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
The Stringer
jenis antarmuka memiliki satu metode saja: String()
. Nilai apa pun yang disimpan dalam nilai antarmuka Stringer
harus memiliki metode ini. Kami juga menciptakan MyType
, dan kami menciptakan metode MyType.String()
dengan penerima pointer . Ini berarti String()
metode adalah dalam metode set dari *MyType
jenis, tapi tidak dalam dari MyType
.
Ketika kami mencoba untuk menetapkan nilai MyType
ke variabel tipe Stringer
, kami mendapatkan kesalahan dalam pertanyaan:
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
Tapi semuanya baik-baik saja jika kita mencoba menetapkan nilai tipe *MyType
ke Stringer
:
s = &m
fmt.Println(s)
Dan kami mendapatkan hasil yang diharapkan (coba di Go Playground ):
something
Jadi persyaratan untuk mendapatkan kesalahan waktu kompilasi ini:
- Nilai tipe beton non-pointer yang ditugaskan (atau diteruskan atau dikonversi)
- Jenis antarmuka yang ditugaskan ke (atau diteruskan ke, atau dikonversi ke)
- Jenis beton memiliki metode antarmuka yang diperlukan, tetapi dengan penerima pointer
Kemungkinan untuk menyelesaikan masalah:
- Pointer ke nilai harus digunakan, yang set metodenya akan menyertakan metode dengan penerima pointer
- Atau tipe penerima harus diubah menjadi non-pointer , sehingga set metode tipe beton non-pointer juga akan berisi metode (dan dengan demikian memenuhi antarmuka). Ini mungkin atau mungkin tidak layak, seolah-olah metode harus mengubah nilai, penerima non-pointer bukan pilihan.
Structs dan embedding
Saat menggunakan struct dan embedding , seringkali bukan "Anda" yang mengimplementasikan antarmuka (menyediakan implementasi metode), tetapi tipe yang Anda sematkan di struct
. Seperti dalam contoh ini:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Sekali lagi, kompilasi kesalahan waktu, karena kumpulan metode MyType2
tidak berisi String()
metode yang disematkan MyType
, hanya kumpulan metode *MyType2
, sehingga yang berikut berfungsi (coba di Go Playground ):
var s Stringer
s = &m2
Kami juga dapat membuatnya berfungsi, jika kami menyematkan *MyType
dan hanya menggunakan non-pointer MyType2
(coba di Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Juga, apa pun yang kami tanamkan (salah satu MyType
atau *MyType
), jika kami menggunakan pointer *MyType2
, itu akan selalu berfungsi (coba di Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Bagian yang relevan dari spec (dari bagian Struct types ):
Diberikan tipe struct S
dan tipe bernama T
, metode yang dipromosikan termasuk dalam set metode struct sebagai berikut:
- Jika
S
berisi bidang anonim T
, set metode S
dan *S
keduanya termasuk metode yang dipromosikan dengan penerima T
. Set metode *S
juga termasuk metode yang dipromosikan dengan penerima *T
.
- Jika
S
berisi bidang anonim *T
, set metode S
dan *S
keduanya termasuk metode yang dipromosikan dengan penerima T
atau *T
.
Jadi dengan kata lain: jika kita menanamkan tipe non-pointer, set metode dari non-pointer embedder hanya mendapatkan metode dengan penerima non-pointer (dari tipe tertanam).
Jika kita menanamkan tipe pointer, set metode dari embedder non-pointer mendapat metode dengan penerima pointer dan non-pointer (dari tipe tertanam).
Jika kita menggunakan nilai pointer ke embedder, terlepas dari apakah tipe yang disematkan adalah pointer atau tidak, set metode pointer ke embedder selalu mendapatkan metode dengan penerima pointer dan non-pointer (dari tipe embedded).
catatan:
Ada kasus yang sangat mirip, yaitu ketika Anda memiliki nilai antarmuka yang membungkus nilai MyType
, dan Anda mencoba mengetikkan menegaskan nilai antarmuka lain dari itu Stringer
,. Dalam hal ini pernyataan tidak akan berlaku karena alasan yang dijelaskan di atas, tetapi kami mendapatkan kesalahan runtime yang sedikit berbeda:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Runtime panic (coba di Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Mencoba mengonversi alih-alih mengetikkan pernyataan, kita mendapatkan kesalahan waktu kompilasi yang sedang kita bicarakan:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
", atau tidak ada . Benarkah begitu? Bisakah saya mencampur berbagai jenis "fungsi anggota", misalnya,func (m *MyType)
&func (m MyType)
?MyType
, jika Anda tidak dapat mengubahMyType
menggunakan metode nilai. Saya mencoba sesuatu seperti ini(&i).(*Stringer)
tetapi tidak berhasil. Apakah itu mungkin?x := i.(MyType)
, dan kemudian Anda dapat memanggil metode dengan penerima pointer di atasnya, misalnyai.String()
, yang merupakan singkatan(&i).String()
yang berhasil karena variabel dialamatkan. Tetapi metode pointer mengubah nilai (nilai runcing) tidak akan tercermin dalam nilai yang dibungkus dengan nilai antarmuka, itu sebabnya tidak masuk akal.*T
tidak termasuk dalam set metodeS
karenaS
mungkin tidak dapat dialamatkan (misalnya nilai pengembalian fungsi atau hasil pengindeksan peta), dan juga karena sering kali hanya salinan yang ada / diterima, dan jika mengambil alamatnya diizinkan, metode ini dengan penerima pointer hanya bisa memodifikasi salinan (Anda akan menganggap aslinya diubah). Lihat jawaban ini sebagai contoh: Menggunakan refleksi SetString .Agar singkat, katakan Anda memiliki kode ini dan Anda memiliki antarmuka Loader dan WebLoader yang mengimplementasikan antarmuka ini.
Jadi kode ini akan memberi Anda kesalahan waktu kompilasi ini
Jadi yang perlu Anda lakukan hanyalah mengubah
webLoader := WebLoader{}
ke berikut:Jadi mengapa itu akan diperbaiki karena Anda mendefinisikan fungsi ini
func (w *WebLoader) Load
untuk menerima penerima pointer. Untuk penjelasan lebih lanjut silakan baca jawaban @icza dan @karorasumber
Kasus lain ketika saya melihat hal semacam ini terjadi adalah jika saya ingin membuat antarmuka di mana beberapa metode akan mengubah nilai internal dan yang lain tidak.
Sesuatu yang kemudian mengimplementasikan antarmuka ini bisa seperti:
Jadi tipe implementasi kemungkinan akan memiliki beberapa metode yang merupakan penerima pointer dan beberapa yang tidak dan karena saya memiliki cukup beragam dari berbagai hal yang GetterSetters saya ingin memeriksa dalam tes saya bahwa mereka semua melakukan yang diharapkan.
Jika saya melakukan sesuatu seperti ini:
Maka saya tidak akan mendapatkan kesalahan "X tidak mengimplementasikan Y (metode Z memiliki penerima pointer)" yang disebutkan di atas (karena ini adalah kesalahan waktu kompilasi) tapi saya akan mengalami hari yang buruk memburu persis mengapa pengujian saya gagal .. .
Sebagai gantinya saya harus memastikan saya melakukan pengecekan tipe menggunakan pointer, seperti:
Atau:
Maka semua senang dengan tes!
Tapi tunggu! Dalam kode saya, mungkin saya memiliki metode yang menerima GetterSetter di suatu tempat:
Jika saya memanggil metode ini dari dalam metode tipe lain, ini akan menghasilkan kesalahan:
Salah satu dari panggilan berikut akan berfungsi:
sumber