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)
?