"<प्रकार> इंटरफ़ेस के लिए सूचक है, इंटरफ़ेस नहीं" भ्रम


105

प्रिय साथी डेवलपर्स,

मुझे यह समस्या है जो मुझे थोड़ी अजीब लगती है। कोड के इस स्निपेट पर एक नज़र डालें:

package coreinterfaces

type FilterInterface interface {
    Filter(s *string) bool
}

type FieldFilter struct {
    Key string
    Val string
}

func (ff *FieldFilter) Filter(s *string) bool {
    // Some code
}

type FilterMapInterface interface {
    AddFilter(f *FilterInterface) uuid.UUID     
    RemoveFilter(i uuid.UUID)                   
    GetFilterByID(i uuid.UUID) *FilterInterface
}

type FilterMap struct {
    mutex   sync.Mutex
    Filters map[uuid.UUID]FilterInterface
}

func (fp *FilterMap) AddFilter(f *FilterInterface) uuid.UUID {
    // Some code
}

func (fp *FilterMap) RemoveFilter(i uuid.UUID) {
    // Some code
}

func (fp *FilterMap) GetFilterByID(i uuid.UUID) *FilterInterface {
    // Some code
}

कुछ अन्य पैकेज पर, मेरे पास निम्नलिखित कोड हैं:

func DoFilter() {
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(fieldfilter) // <--- Exception is raised here
}

रन-टाइम, उल्लिखित लाइन को स्वीकार नहीं करेगा क्योंकि

"fieldfilter का उपयोग नहीं कर सकते हैं (type * coreinterfaces.FieldFilter) प्रकार के रूप में * coreinterfaces.FilterInterface तर्क के लिए fieldint.AddFilter में: * coreinterfaces.FilterInterface इंटरफ़ेस के लिए सूचक है, इंटरफ़ेस नहीं"

हालाँकि, कोड को बदलते समय:

func DoBid() error {
    bs := string(b)
    var ifilterfield coreinterfaces.FilterInterface
    fieldfilter := &coreinterfaces.FieldFilter{Key: "app", Val: "152511"}
    ifilterfield = fieldfilter
    filtermap := &coreinterfaces.FilterMap{}
    _ = filtermap.AddFilter(&ifilterfield)
}

सब कुछ ठीक है और जब आवेदन डिबगिंग होता है तो यह वास्तव में शामिल लगता है

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


जब लाइन में परिवर्तन * FilterInterfaceहो रहा है, तो इसे उठाता है: फ़िल्टर करने के लिए तर्क के रूप में fieldfilter (type coreinterfaces.FieldFilter) का उपयोग नहीं कर सकते हैं । करने के लिए लाइन यह काम करता है। यहाँ क्या हुआ? ऐसा क्यों है? FilterInterface_ = filtermap.AddFilter(fieldfilter)_ = filtermap.AddFilter(&fieldfilter)
0rka

2
क्योंकि इंटरफ़ेस को लागू करने वाले तरीकों में सूचक रिसीवर हैं। एक मान पारित करना, यह इंटरफ़ेस को लागू नहीं करता है; एक पॉइंटर पास करना, यह करता है, क्योंकि विधियां तब लागू होती हैं। आम तौर पर बोलते हुए, इंटरफेस के साथ काम करते समय, आप एक इंटरफ़ेस की अपेक्षा एक फ़ंक्शन के लिए एक स्ट्रक्चर को एक पॉइंटर पास करते हैं। आप लगभग किसी भी परिदृश्य में इंटरफ़ेस के लिए पॉइंटर नहीं चाहते हैं।
एड्रियन

1
मैं आपकी बात को समझता हूं, लेकिन * FilterInterfaceइस इंटरफ़ेस को लागू करने वाली संरचना से पैरामीटर मान को बदलकर , यह कार्य करने के लिए इंटरफेस को पारित करने के विचार को तोड़ता है। मैं जो पूरा करना चाहता था वह उस संरचना से बाध्य नहीं हो रहा था जो मैं कर रहा था, बल्कि वह कोई भी संरचना जो उस इंटरफ़ेस को लागू करता है जिसे मैं उपयोग करना चाहता हूं। आपके द्वारा किए जाने वाले किसी भी कोड परिवर्तन को आप अधिक कुशल या मानकों तक मान सकते हैं? मुझे कुछ कोड समीक्षा सेवाओं का उपयोग करने में खुशी होगी :)
0rka

2
आपके फ़ंक्शन को इंटरफ़ेस तर्क (इंटरफ़ेस के लिए सूचक नहीं) को स्वीकार करना चाहिए। कॉलर को एक पॉइंटर में एक संरचना में पास होना चाहिए जो इंटरफ़ेस को लागू करता है। यह "फ़ंक्शन के पासिंग इंटरफेस के विचार को नहीं तोड़ता है" - फ़ंक्शन अभी भी एक इंटरफ़ेस लेता है, आप एक कॉन्सेप्ट में गुजर रहे हैं जो इंटरफ़ेस को लागू करता है।
एड्रियन

जवाबों:


143

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

type Fooer interface {
    Dummy()
}

type Foo struct{}

func (f Foo) Dummy() {}

func main() {
    var f1 Foo
    var f2 *Foo = &Foo{}

    DoFoo(f1)
    DoFoo(f2)
}

func DoFoo(f Fooer) {
    fmt.Printf("[%T] %+v\n", f, f)
}

आउटपुट:

[main.Foo] {}
[*main.Foo] &{}

https://play.golang.org/p/I7H_pv5H3Xl

दोनों ही मामलों में, fचर DoFooकेवल एक इंटरफ़ेस है, इंटरफ़ेस के लिए सूचक नहीं है। हालांकि, भंडारण करते समय f2, इंटरफ़ेस एक संरचना के लिए एक संकेतक रखता है Foo

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

हालांकि, इंटरफेस पर एक सीमा है। यदि आप किसी संरचना को सीधे किसी इंटरफ़ेस में पास करते हैं, तो इंटरफ़ेस को पूरा करने के लिए उस प्रकार के केवल वैल्यू मेथड्स (यानी func (f Foo) Dummy(), नहीं func (f *Foo) Dummy()) का उपयोग किया जा सकता है। ऐसा इसलिए है क्योंकि आप इंटरफ़ेस में मूल संरचना की एक प्रति संग्रहीत कर रहे हैं, इसलिए सूचक विधियों में अप्रत्याशित प्रभाव होंगे (अर्थात मूल संरचना को बदलने में असमर्थ)। इस प्रकार अंगूठे का डिफ़ॉल्ट नियम इंटरफेस को संरचनाओं में संग्रहीत करना है , जब तक कि कोई सम्मोहक कारण न हो।

विशेष रूप से अपने कोड के साथ, यदि आप AddFilter फ़ंक्शन हस्ताक्षर को इसमें बदलते हैं:

func (fp *FilterMap) AddFilter(f FilterInterface) uuid.UUID

और GetFilterByID हस्ताक्षर:

func (fp *FilterMap) GetFilterByID(i uuid.UUID) FilterInterface

आपका कोड अपेक्षित रूप से काम करेगा। fieldfilterप्रकार का है *FieldFilter, जो FilterInterfaceइंटरफ़ेस प्रकार को पूर्ण करता है, और इस प्रकार AddFilterइसे स्वीकार करेगा।

यह समझने के लिए कुछ अच्छे संदर्भ हैं कि कैसे तरीके, प्रकार और इंटरफेस काम करते हैं और एक दूसरे के साथ गो में एकीकृत होते हैं:


"ऐसा इसलिए है क्योंकि आप इंटरफ़ेस में मूल संरचना की एक प्रति संग्रहीत कर रहे हैं, इसलिए सूचक विधियों में अप्रत्याशित प्रभाव होंगे (अर्थात मूल संरचना को बदलने में असमर्थ)" - यह सीमा के कारण के रूप में समझ में नहीं आता है। सब के बाद, केवल एक कॉपी सभी इंटरफ़ेस में संग्रहीत किया गया हो सकता है।
WPWoodJr

आपके जवाब का कोई मतलब नहीं है। आप यह मान रहे हैं कि जिस स्थान पर इंटरफ़ेस में संग्रहीत ठोस प्रकार बदल जाता है, वह नहीं बदलता है जब आप वहां संग्रहीत करते हैं, जो कि मामला नहीं है, और यह स्पष्ट होना चाहिए यदि आप एक अलग मेमोरी लेआउट के साथ कुछ स्टोर कर रहे हैं। मेरी पॉइंटर टिप्पणी के बारे में आपको जो नहीं मिल रहा है, वह यह है कि एक ठोस प्रकार पर एक पॉइंटर रिसीवर विधि हमेशा उस रिसीवर को संशोधित कर सकती है जिस पर उसे कॉल किया जा रहा है। एक इंटरफ़ेस में संग्रहीत एक मूल्य एक कॉपी को मजबूर करता है जिसे आप तब संदर्भ नहीं दे सकते हैं, इसलिए सूचक रिसीवर मूल, अवधि को संशोधित नहीं कर सकते हैं
केडीज

5
GetFilterByID(i uuid.UUID) *FilterInterface

जब मुझे यह त्रुटि मिलती है, तो यह आमतौर पर इसलिए होता है क्योंकि मैं एक इंटरफ़ेस के बजाय एक संकेतक को एक इंटरफ़ेस में निर्दिष्ट कर रहा हूं (जो वास्तव में मेरी संरचना के लिए एक संकेतक होगा जो इंटरफ़ेस को पूरा करता है)।

* इंटरफ़ेस {...} के लिए एक वैध उपयोग है, लेकिन आमतौर पर मैं सिर्फ यह सोच रहा हूं कि 'यह एक इंटरफ़ेस है' के बजाय यह एक इंटरफ़ेस है जो उस कोड में एक पॉइंटर होता है जो मैं लिख रहा हूं '

बस इसे वहाँ से बाहर फेंक दिया क्योंकि स्वीकृत जवाब, हालांकि विस्तृत, ने मुझे समस्या निवारण में मदद नहीं की।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.