X, Y'yi uygulamıyor (… yönteminde bir işaretçi alıcısı var) [kapalı]


217

Bu " X, Y'yi uygulamıyor (... yöntemin bir işaretçi alıcısı var) " meselesinde zaten birkaç Soru ve Cevap var , ama bana göre farklı şeyler hakkında konuşuyorlar ve benim özel durumuma uygulanmıyorlar.

Yani, soruyu çok spesifik hale getirmek yerine, onu geniş ve soyut hale getiriyorum - Görünüşe göre bu hatayı meydana getirebilecek birkaç farklı durum var, birisi özetleyebilir mi lütfen?

Yani, problemden nasıl kaçınılır ve ortaya çıkarsa olasılıklar nelerdir? Teşekkürler.

Yanıtlar:


397

Bu derleme zamanı hatası, somut bir türü bir arabirim türüne atamaya veya geçirmeye (veya dönüştürmeye) çalıştığınızda ortaya çıkar ; ve türün kendisi arabirimi uygulamaz, yalnızca türe bir gösterici .

Bir örnek görelim:

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

func (m *MyType) String() string { return m.value }

StringerArayüz tipi bir yöntem, yalnızca var String(). Bir arayüz değerinde saklanan herhangi bir değer Stringerbu yönteme sahip olmalıdır. Ayrıca a oluşturduk MyTypeve işaretçi alıcısı MyType.String()ile bir yöntem oluşturduk . Bu, yöntemin türün yöntem kümesinde olduğu , ancak türünde olmadığı anlamına gelir .String()*MyTypeMyType

Bir MyTypetür değişkenine bir değer atamaya çalıştığımızda Stringer, söz konusu hatayı alırız:

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)

Biz türünde bir değer atamak deneyin eğer herşey yolunda *MyTypeiçin Stringer:

s = &m
fmt.Println(s)

Ve beklenen sonucu elde ederiz ( Go Playground'da deneyin ):

something

Bu derleme zamanı hatasını almak için gerekenler:

  • Atanan (veya geçirilen veya dönüştürülen) işaretçi olmayan somut türden bir değer
  • Atanan (veya iletilen veya dönüştürülen) bir arabirim türü
  • Somut tip, arayüzün gerekli yöntemine sahiptir, ancak bir işaretçi alıcısı ile

Sorunu çözme olasılıkları:

  • Değere yönelik bir işaretçi kullanılmalıdır, bu işaretçi, yöntem kümesi, işaretçi alıcı ile yöntemi içerecektir.
  • Veya alıcı türü işaretçi olmayan olarak değiştirilmelidir , bu nedenle işaretçi olmayan somut türün yöntem kümesi de yöntemi içerecektir (ve böylece arayüzü karşılayacaktır). Yöntemin değeri değiştirmesi gerekiyorsa, işaretçi olmayan bir alıcı bir seçenek değildir, bu uygulanabilir veya olmayabilir.

Yapılar ve gömme

Kullanırken yapılar ve katıştırma , genellikle "siz" bir arabirim uygulamak (a yöntem uygulanmasını sağlamak) değil, ama bir tür aşağıdaki konularda gömülecek struct. Bu örnekteki gibi:

type MyType2 struct {
    MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: m}

var s Stringer
s = m2 // Compile-time error again

Yine, derleme zamanı hatası, çünkü yöntem kümesi gömülü yöntemin yöntemini MyType2içermez , yalnızca yöntem kümesini içerir , bu nedenle aşağıdakiler çalışır ( Go Playground'da deneyin ):String()MyType*MyType2

var s Stringer
s = &m2

*MyTypeYalnızca işaretçi olmayan bir işaretçi yerleştirirsek ve kullanırsak MyType2( Go Playground'da deneyin) çalışmasını da sağlayabiliriz :

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = m2

Ayrıca, ne yerleştirirsek (ya MyTypeda *MyType), bir işaretçi kullanırsak *MyType2, her zaman işe yarar ( Go Playground'da deneyin ):

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = &m2

Spesifikasyondaki ilgili bölüm ( Yapı türleri bölümünden ):

Bir yapı türü Sve adı verilen bir tür verildiğinde T, yükseltilen yöntemler yapının yöntem kümesine aşağıdaki gibi dahil edilir:

  • Eğer Sanonim alan içeren T, yöntem setleri Sve *Sher iki alıcılı yöntemleri terfi içerir T. Yöntem seti, *Saynı zamanda, alıcı ile yükseltilmiş yöntemleri de içerir *T.
  • Eğer Sanonim alan içeren *T, yöntem setleri Sve *Sher iki alıcılı yöntemleri terfi içerir Tveya *T.

Yani başka bir deyişle: işaretçi olmayan bir türü gömersek, işaretçi olmayan gömücünün yöntem kümesi yalnızca işaretçi olmayan alıcılara sahip yöntemleri alır (gömülü türden).

Bir işaretçi türü gömersek, işaretçi olmayan gömücünün yöntem kümesi, hem işaretçi hem de işaretçi olmayan alıcılarla (gömülü türden) yöntemler alır.

Gömülü tipin işaretçi olup olmadığına bakılmaksızın, gömücü için bir işaretçi değeri kullanırsak, gömülü tipin işaretçi seti metodu her zaman hem işaretçi hem de işaretçi olmayan alıcılarla (gömülü tipten) yöntemleri alır.

Not:

Çok benzer bir durum var, yani bir değerini saran bir arabirim değeriniz olduğunda ve ondan başka bir arabirim değeri atamayaMyType çalıştığınızda ,. Bu durumda, iddia yukarıda açıklanan nedenlerden dolayı geçerli olmayacaktır, ancak biraz farklı bir çalışma zamanı hatası alıyoruz:Stringer

m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))

Çalışma zamanı paniği ( Go Playground'da deneyin ):

panic: interface conversion: main.MyType is not main.Stringer:
    missing method String

Tip assert yerine dönüştürmeye çalışırken, bahsettiğimiz derleme zamanı hatasını alıyoruz:

m := MyType{value: "something"}

fmt.Println(Stringer(m))

Son derece kapsamlı cevap için teşekkürler. Garip bir şekilde geç yanıt verdiğim için özür dilerim, SO bildirimini almadım. Araştırdığım bir durum, yanıt "üye işlevlerinin" ya tüm işaretçi türleri olması gerektiğiydi , örneğin, " func (m *MyType)" veya hiçbiri . Öyle mi? Farklı "üye işlevleri" türlerini karıştırabilir miyim, örneğin func (m *MyType)& func (m MyType)?
xpt

3
@xpt İşaretçi ve işaretçi olmayan alıcıları karıştırabilirsiniz, hepsini aynı yapmak zorunda değilsiniz. İşaretçi alıcısıyla 19 yönteminiz varsa ve işaretçi olmayan alıcıyla bir tane yaparsanız garip. Ayrıca, onları karıştırmaya başlarsanız, hangi yöntemlerin hangi türlerin yöntem kümelerinin parçası olduğunu izlemeyi zorlaştırır. Bu cevapta daha fazla ayrıntı: Golang'da değer alıcısı mı yoksa İşaretçi alıcısı mı?
icza

Değer yöntemlerini kullanmak için MyTypedeğiştiremiyorsanız, bir değer {} saran bir arayüzle "Not:" sonunda bahsedilen sorunu gerçekten nasıl çözersiniz? MyTypeBöyle bir şey denedim (&i).(*Stringer)ama işe yaramıyor. Hatta mümkün mü?
Joel Edström

1
@ JoelEdström Evet, mümkün, ancak çok az mantıklı. Örneğin, işaretçi olmayan tipin değerini yazabilir ve bir değişkende saklayabilirsiniz, örneğin x := i.(MyType), ve daha sonra üzerinde işaretçi alıcısı olan yöntemleri çağırabilirsiniz, örneğin i.String(), (&i).String()değişkenler adreslenebilir olduğu için başarılı olan bir kısaltmadır . Ancak değeri (sivri değeri) değiştiren işaretçi yöntemi, arayüz değerine sarılmış değere yansıtılmayacaktır, bu yüzden çok az anlam ifade eder.
icza

1
Arasında @DeepNightTwo Yöntemleri *Työntemi kümesine dahil edilmez Sçünkü Skudreti adreslenebilir (örn işlev dönüş değeri veya harita indeksleme sonuçlanan) olmak değil, aynı zamanda çünkü genellikle sadece bir kopyası mevcut / aldı ve adresini alarak izin verilmesi halinde, yöntem işaretçi ile alıcı yalnızca kopyayı değiştirebilir (orijinalin değiştirildiğini düşündüğünüz gibi karışıklık). Bir örnek için bu yanıta bakın: SetString yansımasını kullanma .
icza

37

Kısa tutmak için, bu koda sahip olduğunuzu ve bir Yükleyici arayüzüne ve bu arayüzü uygulayan bir Web Yükleyiciye sahip olduğunuzu varsayalım.

package main

import "fmt"

// Loader defines a content loader
type Loader interface {
    Load(src string) string
}

// WebLoader is a web content loader
type WebLoader struct{}

// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
    return fmt.Sprintf("I loaded this page %s", src)
}

func main() {
    webLoader := WebLoader{}
    loadContent(webLoader)
}

func loadContent(loader Loader) {
    loader.Load("google.com")
}

Bu kod size bu derleme zamanı hatasını verecektir.

./main.go:20:13: loadContent argümanında Loader türü olarak webLoader (WebLoader türü) kullanılamaz: WebLoader, Loader uygulamıyor (Load yönteminde işaretçi alıcısı var)

Yani yapmanız gereken tek şey webLoader := WebLoader{}aşağıdakilere geçmektir:

webLoader := &WebLoader{} 

Öyleyse neden bu işlevi func (w *WebLoader) Loadbir işaretçi alıcısını kabul edecek şekilde tanımladığınız için düzeltilecek . Daha fazla açıklama için lütfen @icza ve @karora cevaplarını okuyun


9
Şimdiye kadar anlaşılması en kolay yorum buydu. Ve karşı karşıya olduğum sorunu doğrudan
çözdüm

@ Maxs728 Kabul edildi, pek çok Go probleminin yanıtlarında oldukça nadir.
milosmns

6

Bu tür bir şeyin olduğunu gördüğüm diğer bir durum, bazı yöntemlerin dahili bir değeri değiştireceği ve diğerlerinin değiştirmeyeceği bir arayüz oluşturmak istememdir.

type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}

Daha sonra bu arayüzü uygulayan bir şey şöyle olabilir:

type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}

Bu nedenle, uygulama türü muhtemelen işaretçi alıcıları olan ve olmayan bazı yöntemlere sahip olacak ve GetterSetters olan bu çeşitli şeylere sahip olduğum için, testlerimde hepsinin beklenenleri yaptığını kontrol etmek istiyorum.

Böyle bir şey yapacak olsaydım:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}

O zaman yukarıda bahsedilen "X, Y'yi uygulamıyor (Z yönteminde işaretçi alıcısı var)" hatasını almayacağım (çünkü bu bir derleme zamanı hatasıdır), ancak testimin tam olarak neden başarısız olduğunu takip eden kötü bir gün geçireceğim .. .

Bunun yerine tür kontrolünü bir işaretçi kullanarak yaptığımdan emin olmalıyım, örneğin:

var f interface{} = new(&MyTypeA)
 ...

Veya:

myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...

O zaman herkes testlerden memnun!

Fakat bekle! Kodumda, bir yerlerde GetterSetter'ı kabul eden yöntemlerim olabilir:

func SomeStuff(g GetterSetter, x int) int {
    if x > 10 {
        return g.GetVal() + 1
    }
    return g.GetVal()
}

Bu yöntemleri başka bir tür yönteminin içinden çağırırsam, bu hatayı oluşturacaktır:

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

Aşağıdaki çağrılardan biri işe yarayacaktır:

func (m *MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.