X, Y को लागू नहीं करता है (… विधि में एक पॉइंटर रिसीवर है) [बंद]


201

पहले से ही कई प्रश्नोत्तर हैं। इस पर " X, Y को लागू नहीं करता है ... (विधि के पास एक पॉइंटर रिसीवर है) " बात है, लेकिन मेरे लिए, वे अलग-अलग चीजों के बारे में बात कर रहे हैं, और मेरे विशिष्ट मामले पर लागू नहीं होते हैं।

इसलिए, प्रश्न को बहुत विशिष्ट बनाने के बजाय, मैं इसे व्यापक और सार बना रहा हूं - ऐसा लगता है कि कई अलग-अलग मामले हैं जो इस त्रुटि को कर सकते हैं, क्या कोई इसे सारांशित कर सकता है?

यानी, समस्या से कैसे बचें, और यदि ऐसा होता है, तो क्या संभावनाएं हैं? धन्यवाद।

जवाबों:


365

यह संकलन-समय त्रुटि तब उत्पन्न होती है जब आप इंटरफ़ेस प्रकार के लिए एक ठोस प्रकार को असाइन या पास (या परिवर्तित) करने का प्रयास करते हैं ; और प्रकार ही इंटरफ़ेस को लागू नहीं करता है, केवल एक सूचक प्रकार के लिए

आइए एक उदाहरण देखें:

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

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

Stringerइंटरफ़ेस प्रकार केवल एक विधि है: String()। इंटरफ़ेस मान में संग्रहीत किसी भी मान में Stringerयह विधि होनी चाहिए। हमने एक भी बनाया MyType, और हमने पॉइंटर रिसीवर के MyType.String()साथ एक विधि बनाई । इसका मतलब यह है कि विधि प्रकार के विधि सेट में है , लेकिन इसमें नहीं है ।String()*MyTypeMyType

जब हम MyTypeकिसी वैरिएबल के प्रकार का मान निर्दिष्ट करने का प्रयास करते हैं Stringer, तो हमें प्रश्न में त्रुटि मिलती है:

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)

लेकिन सब कुछ ठीक है अगर हम टाइप *MyTypeकरने के लिए एक मान निर्दिष्ट करने की कोशिश करते हैं Stringer:

s = &m
fmt.Println(s)

और हम अपेक्षित परिणाम प्राप्त करते हैं (इसे खेल के मैदान पर आज़माएं ):

something

तो इस संकलन-समय त्रुटि प्राप्त करने के लिए आवश्यकताओं:

  • गैर-पॉइंटर कंक्रीट प्रकार का एक मूल्य सौंपा जा रहा है (या पारित या परिवर्तित)
  • एक इंटरफ़ेस प्रकार जिसे सौंपा जा रहा है (या उसे पास या परिवर्तित किया गया है)
  • कंक्रीट प्रकार में इंटरफ़ेस की आवश्यक विधि है, लेकिन एक पॉइंटर रिसीवर के साथ

समस्या को हल करने के लिए संभावनाएँ:

  • मूल्य के लिए एक संकेतक का उपयोग किया जाना चाहिए, जिसकी विधि सेट में सूचक रिसीवर के साथ विधि शामिल होगी
  • या रिसीवर प्रकार को गैर-पॉइंटर में बदलना होगा , इसलिए गैर-पॉइंटर कंक्रीट प्रकार के सेट में विधि भी शामिल होगी (और इस प्रकार इंटरफ़ेस को संतुष्ट करें)। यह व्यवहार्य हो सकता है या नहीं भी हो सकता है, क्योंकि यदि विधि को मूल्य को संशोधित करना है, तो एक गैर-पॉइंटर रिसीवर एक विकल्प नहीं है।

संरचनाएं और एम्बेडिंग

संरचना और एम्बेडिंग का उपयोग करते समय , अक्सर यह "आप" नहीं होता है जो एक इंटरफ़ेस लागू करता है (एक विधि कार्यान्वयन प्रदान करता है), लेकिन एक प्रकार जिसे आप अपने में एम्बेड करते हैं struct। इस उदाहरण में पसंद करें:

type MyType2 struct {
    MyType
}

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

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

फिर से, संकलन-समय त्रुटि, क्योंकि विधि के सेट में एम्बेडेड MyType2की String()विधि नहीं होती है MyType, केवल विधि सेट की जाती है *MyType2, इसलिए निम्न कार्य करता है (इसे Go Playground पर आज़माएं ):

var s Stringer
s = &m2

हम इसे काम भी कर सकते हैं, अगर हम *MyTypeकेवल एक गैर-पॉइंटर को एम्बेड और उपयोग करते हैं MyType2(इसे गो प्लेग्राउंड पर आज़माएं ):

type MyType2 struct {
    *MyType
}

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

var s Stringer
s = m2

इसके अलावा, जो कुछ भी हम एम्बेड (या तो MyTypeया *MyType), अगर हम एक सूचक का उपयोग *MyType2, यह होगा हमेशा काम (इस पर कोशिश जाओ खेल का मैदान ):

type MyType2 struct {
    *MyType
}

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

var s Stringer
s = &m2

युक्ति से प्रासंगिक खंड (अनुभाग संरचना प्रकार से ):

एक संरचनात्मक प्रकार Sऔर एक प्रकार का नाम Tदिया गया है , पदोन्नत तरीकों को संरचना के सेट में निम्नानुसार शामिल किया गया है:

  • यदि Sएक गुमनाम फ़ील्ड है T, की विधि सेट Sऔर *Sदोनों पदोन्नत रिसीवर के साथ तरीकों में शामिल हैं T*Sरिसीवर के साथ प्रोमोटेड विधियों में सेट विधि भी शामिल है *T
  • यदि Sएक गुमनाम फ़ील्ड है *T, की विधि सेट Sऔर *Sदोनों पदोन्नत रिसीवर के साथ तरीकों में शामिल हैं Tया *T

तो दूसरे शब्दों में: यदि हम एक गैर-पॉइंटर प्रकार को एम्बेड करते हैं, तो नॉन-पॉइंटर एंबेडर की विधि सेट केवल गैर-पॉइंटर रिसीवर (एम्बेडेड प्रकार से) के साथ विधियां प्राप्त करती है।

यदि हम एक पॉइंटर प्रकार को एम्बेड करते हैं, तो नॉन-पॉइंटर एंबेडर की विधि सेट को पॉइंटर और नॉन-पॉइंटर रिसीवर्स (एम्बेडेड प्रकार से) दोनों के तरीके मिलते हैं।

यदि हम एंबेडर के लिए एक पॉइंटर मान का उपयोग करते हैं, तो इसकी परवाह किए बिना कि एंबेडेड प्रकार पॉइंटर है या नहीं, एंबेडर को पॉइंटर के सेट को हमेशा पॉइंटर और नॉन-पॉइंटर रिसीवर्स (एम्बेडेड प्रकार से) दोनों के तरीके मिलते हैं।

ध्यान दें:

वहाँ आप एक इंटरफेस मूल्य जिनमें से एक मूल्य लपेटता है अर्थात् जब एक बहुत ही इसी तरह की स्थिति है, MyTypeहै, और आप करने की कोशिश ज़ोर टाइप , यह से दूसरे इंटरफेस मूल्य Stringer। इस मामले में, ऊपर वर्णित कारणों के लिए जोर नहीं होगा, लेकिन हमें थोड़ा अलग रनटाइम त्रुटि मिलती है:

m := MyType{value: "something"}

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

रनटाइम घबराहट (इसे खेल के मैदान पर आज़माएं ):

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

टाइप एस्टर के बजाय बदलने की कोशिश कर रहे हैं, हम संकलन-समय त्रुटि प्राप्त कर रहे हैं:

m := MyType{value: "something"}

fmt.Println(Stringer(m))

अत्यंत व्यापक उत्तर के लिए धन्यवाद। देर से जवाब देने के लिए क्षमा करें अजीब रूप से मुझे एसओ अधिसूचना नहीं मिली। एक मामला जो मैंने खोजा, उसका उत्तर था कि "सदस्य फ़ंक्शंस" या तो सभी पॉइंटर प्रकार के होने चाहिए , जैसे, " func (m *MyType)", या कोई भी नहीं । ऐसा है क्या? क्या मैं "सदस्य फ़ंक्शंस", func (m *MyType)और func (m MyType)?
एक्सपी

3
@ x आप पॉइंटर और नॉन-पॉइंटर रिसीवर्स को मिक्स कर सकते हैं, यह सभी को समान बनाने की आवश्यकता नहीं है। यह सिर्फ अजीब है अगर आपके पास पॉइंटर रिसीवर के साथ 19 विधियां हैं और आप गैर-पॉइंटर रिसीवर के साथ एक बनाते हैं। इससे यह पता लगाना भी मुश्किल हो जाता है कि कौन सी विधियाँ किस प्रकार की विधि का हिस्सा हैं यदि आप उन्हें मिलाना शुरू करते हैं। इस उत्तर में अधिक जानकारी: मूल्य रिसीवर बनाम गोलंग में सूचक रिसीवर?
इकाजा

आप वास्तव में "नोट:" में अंत में उल्लिखित समस्या को कैसे हल करते हैं MyType, यदि आप MyTypeमूल्य विधियों का उपयोग करने के लिए नहीं बदल सकते हैं, तो {} के एक मूल्य को लपेटते हुए । मैंने ऐसा कुछ करने की कोशिश की (&i).(*Stringer)लेकिन यह काम नहीं कर रहा है। क्या यह भी संभव है?
जोएल एडस्ट्रम जुएल

1
@ JoelEdström हां, यह संभव है, लेकिन यह थोड़ा समझ में आता है। उदाहरण के लिए, आप गैर-पॉइंटर प्रकार के मूल्य को टाइप कर सकते हैं और इसे एक चर, उदाहरण के लिए स्टोर x := i.(MyType)कर सकते हैं, और फिर आप उस पर पॉइंटर रिसीवर के साथ तरीकों को कॉल कर सकते हैं, उदाहरण के लिए i.String(), एक शॉर्टहैंड है (&i).String()जिसके लिए सफल होता है क्योंकि चर पता करने योग्य होते हैं। लेकिन मान को बदलने वाला पॉइंटर विधि (इंगित मूल्य) इंटरफ़ेस मूल्य में लिपटे मूल्य में परिलक्षित नहीं होगा, यही कारण है कि यह थोड़ा समझ में आता है।
आईसीए

1
@DeepNightTwo की विधियाँ *Tविधि सेट में शामिल नहीं हैं Sक्योंकि Sपता योग्य नहीं हो सकता है (जैसे कि फ़ंक्शन रिटर्न मान या मानचित्र अनुक्रमण का परिणाम), और यह भी क्योंकि अक्सर केवल एक प्रति ही मौजूद होती है / प्राप्त की जाती है, और यदि उसका पता लेने की अनुमति है, तो विधि सूचक रिसीवर के साथ केवल कॉपी को संशोधित कर सकता है (भ्रम के रूप में आप मानेंगे कि मूल संशोधित है)। इस उत्तर को एक उदाहरण के लिए देखें: प्रतिबिंब सेटस्ट्रिंग का उपयोग करना
आईसीएजे

33

इसे छोटा रखने के लिए, मान लें कि आपके पास यह कोड है और आपके पास एक लोडर इंटरफ़ेस और एक WebLoader है जो इस इंटरफ़ेस को लागू करता है।

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")
}

तो यह कोड आपको यह संकलन समय त्रुटि देगा

./main.go:20:13: लोड करने के तर्क के रूप में वेब लोडर (प्रकार WebLoader) का उपयोग नहीं कर सकता

तो आपको केवल करने की आवश्यकता है जिसे webLoader := WebLoader{}निम्नलिखित में बदलना है:

webLoader := &WebLoader{} 

तो यह ठीक क्यों होगा क्योंकि आप func (w *WebLoader) Loadएक पॉइंटर रिसीवर को स्वीकार करने के लिए इस फ़ंक्शन को परिभाषित करते हैं। अधिक स्पष्टीकरण के लिए कृपया @icza और @karora उत्तर पढ़ें


6
अब तक यह समझने में सबसे आसान टिप्पणी थी। और सीधे उस मुद्दे को हल कर दिया, जिसका मैं सामना कर रहा था ..
Maxs728

@ Maxs728 सहमत, कई समस्याओं के जवाब में काफी असामान्य है।
मिल्स

6

एक और मामला जब मैंने इस तरह की बात को देखा है, अगर मैं एक इंटरफ़ेस बनाना चाहता हूं, जहां कुछ तरीके आंतरिक मूल्य को संशोधित करेंगे और अन्य नहीं करेंगे।

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

कुछ ऐसा है जो तब इस इंटरफ़ेस को लागू करता है:

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
}

इसलिए लागू करने के प्रकार में कुछ तरीके होंगे जो पॉइंटर रिसीवर हैं और कुछ जो नहीं हैं और चूंकि मेरे पास इन विभिन्न चीजों में से एक किस्म है जो गेटटरसेटर्स हैं मैं अपने परीक्षणों में जांचना चाहूंगा कि वे सभी अपेक्षित हैं।

अगर मुझे ऐसा कुछ करना था:

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

तब मैं मिल जाएगा नहीं ऊपर उल्लिखित "एक्स वाई को लागू नहीं करता है (जेड विधि सूचक रिसीवर है)" त्रुटि (क्योंकि यह एक संकलन समय त्रुटि है), लेकिन मैं होगा एक बुरा दिन है नीचे का पीछा करते हुए वास्तव में क्यों अपने परीक्षण विफल हो रहा है .. ।

इसके बजाय मुझे यह सुनिश्चित करना होगा कि मैं एक पॉइंटर का उपयोग करके प्रकार की जांच करूं, जैसे:

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

या:

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

फिर सभी परीक्षणों से खुश हैं!

लेकिन रुकें! मेरे कोड में, शायद मेरे पास ऐसे तरीके हैं जो एक गेट्सटर को कहीं स्वीकार करते हैं:

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

यदि मैं इन विधियों को किसी अन्य प्रकार की विधि के अंदर से कॉल करता हूं, तो यह त्रुटि उत्पन्न करेगा:

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

निम्नलिखित में से कोई एक कॉल काम करेगा:

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

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.