2015-03-19 10 views
25

मैं समझता हूँ करने के लिए कैसे सही ढंग से है कि कुछ को आश्वस्त करने में विफल इस मामले में nil नहीं है: अगर है कि मूल्य वास्तव में nil हैशून्य मान छिपाई जा रही है, समझ क्यों golang यहाँ विफल रहता है

package main 

type shower interface { 
    getWater() []shower 
} 

type display struct { 
    SubDisplay *display 
} 

func (d display) getWater() []shower { 
    return []shower{display{}, d.SubDisplay} 
} 

func main() { 
    // SubDisplay will be initialized with null 
    s := display{} 
    // water := []shower{nil} 
    water := s.getWater() 
    for _, x := range water { 
    if x == nil { 
     panic("everything ok, nil found") 
    } 

    //first iteration display{} is not nil and will 
    //therefore work, on the second iteration 
    //x is nil, and getWater panics. 
    x.getWater() 
    } 
} 

एक ही रास्ता मैंने पाया जाँच करने के लिए प्रतिबिंब का उपयोग करके।

क्या यह वास्तव में वांछित व्यवहार है? या क्या मैं अपने कोड में कुछ बड़ी गलती देखने में विफल रहता हूं?

Play link here

+3

यह एक त्रुटि के रूप में 'त्रुटि' इंटरफ़ेस का उपयोग करते हुए FAQ में है: https://golang.org/doc/faq#nil_error – JimB

उत्तर

41

समस्या है कि यहाँ shower एक interface प्रकार है। गो में इंटीफेस प्रकार वास्तविक मान रखते हैं और इसके गतिशील प्रकार। इसके बारे में अधिक जानकारी: The Laws of Reflection #The representation of an interface

आपके द्वारा लौटाई गई टुकड़ा में 2 गैर- nil मान शामिल हैं। दूसरा मान एक इंटरफ़ेस मान है, एक (मान; प्रकार) जोड़ी nil मान और *display प्रकार है। Go Language Specification: Comparison operators से क्यूटोइंग:

इंटरफ़ेस मान तुलनीय हैं। दो इंटरफ़ेस मान बराबर होते हैं यदि उनके पास समान गतिशील प्रकार और समान गतिशील मान होते हैं या दोनों के पास nil मान है।

तो यदि आप इसकी तुलना nil से करते हैं, तो यह false होगा। आप जोड़ी (nil;*display) का प्रतिनिधित्व एक अंतरफलक मूल्य की तुलना करते हैं, यह true हो जाएगा:

if x == (*display)(nil) { 
    panic("everything ok, nil found") 
} 

यह unfeasable लगता है के रूप में आप वास्तविक प्रकार इंटरफ़ेस रखती है पता करने के लिए होगा।

इस तरह इसे क्यों लागू किया गया है?

अन्य ठोस प्रकारों (गैर-इंटरफेस) के विपरीत इंटरफेस विभिन्न ठोस प्रकारों (विभिन्न स्थिर प्रकार) के मूल्य रख सकते हैं। रनटाइम को इंटरफ़ेस प्रकार के चर में संग्रहीत मान के गतिशील या रनटाइम-प्रकार को जानने की आवश्यकता होती है।

एक interface सिर्फ एक विधि सेट, किसी भी प्रकार यह लागू करता है, तो एक ही तरीके का प्रकार का method set का हिस्सा हैं है। ऐसे प्रकार हैं जो nil नहीं हो सकते हैं, उदाहरण के लिए struct या int के साथ एक कस्टम प्रकार इसके अंतर्निहित प्रकार के रूप में। इन मामलों में आपको उस विशिष्ट प्रकार के nil मान को स्टोर करने में सक्षम होने की आवश्यकता नहीं होगी।

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

कुछ लोग कहते हैं कि गो के इंटरफेस गतिशील रूप से टाइप किए गए हैं, लेकिन यह भ्रामक है। वे स्थिर रूप से टाइप किए गए हैं: इंटरफ़ेस प्रकार का एक चर हमेशा एक ही स्थिर प्रकार होता है, और भले ही रन टाइम पर इंटरफ़ेस चर में संग्रहीत मान प्रकार बदल सकता है, वह मान हमेशा इंटरफ़ेस को संतुष्ट करेगा।

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

अपने उदाहरण में भ्रम की स्थिति तथ्यों से उत्पन्न होती है कि:

  • आप एक इंटरफेस प्रकार के रूप में एक मूल्य करना चाहते हैं (shower)
  • लेकिन मूल्य आप टुकड़ा में संग्रहीत करना चाहते हैं की नहीं है प्रकार shower लेकिन एक ठोस प्रकार

तो जब आप shower टुकड़ा में एक *display प्रकार शब्दों में कहें, एक अंतरफलक मूल्य बनाया जाएगा, जो की एक जोड़ी है (मूल्य; प्रकार) जहां मूल्य ०१२३४६१५१७ हैऔर प्रकार *display है। जोड़ी के अंदर मूल्यnil होगा, न कि इंटरफ़ेस मूल्य स्वयं। यदि आप स्लाइस में nil मान डाल देंगे, तो इंटरफ़ेस मान स्वयंnil होगा और एक स्थिति x == niltrue होगी। Playground

type MyErr string 

func (m MyErr) Error() string { 
    return "big fail" 
} 

func doSomething(i int) error { 
    switch i { 
    default: 
     return nil // This is the trivial true case 
    case 1: 
     var p *MyErr 
     return p // This will be false 
    case 2: 
     return (*MyErr)(nil) // Same as case 1 
    case 3: 
     var err error // Zero value is nil for the interface 
     return err // This will be true because err is already interface type 
    case 4: 
     var p *MyErr 
     return error(p) // This will be false because the interface points to a 
         // nil item but is not nil itself. 
    } 
} 

func main() { 
    for i := 0; i <= 4; i++ { 
     err := doSomething(i) 
     fmt.Println(i, err, err == nil) 
    } 
} 

आउटपुट::

0 <nil> true 
1 <nil> false 
2 <nil> false 
3 <nil> true 
4 <nil> false 

मामले 2 में एक nil सूचक दिया जाता है, लेकिन पहले यह एक इंटरफ़ेस प्रकार में बदल जाती है (

प्रदर्शन

इस उदाहरण देखें error) तो एक इंटरफ़ेस मान बनाया गया है जिसमेंहैमान और प्रकार *MyErr, इसलिए इंटरफ़ेस मान nil नहीं है।

+0

यह एक सुंदर है ... बड़ी विफलता नहीं है? मैं एक पुस्तकालय कैसे लिख सकता हूं जो इस तरह इंटरफेस प्रदान करता है? मैं आश्वस्त कर सकता हूं कि यह सही ढंग से काम करता है यदि उपयोगकर्ता शून्य मान के साथ सही प्रकार प्रदान करते हैं। मुझे लगता है कि यदि x == (शॉवर) (शून्य) समझ में आएगा लेकिन यह सिर्फ चौंकाने वाला है .. – sharpner

+5

इसके बारे में कुछ भी चौंकाने वाला नहीं है। आपकी वापसी दो इंटरफ़ेस मानों का एक टुकड़ा है, दोनों गैर-शून्य हैं। उन गैर-शून्य इंटरफेस मानों में से एक में शून्य मूल्य होता है। अंतरफलक मान _has_ कुछ भी शामिल करने के लिए गैर-शून्य होने के लिए, यहां तक ​​कि एक शून्य भी। आप इसे आईसीज़ा सुझाए गए जैसे ठीक कर सकते हैं, या अपने एपीआई को फिर से डिजाइन कर सकते हैं, उदा। इंटरफेस का एक टुकड़ा वापस नहीं। – Volker

+2

@sharpner आप एक लाइब्रेरी प्रदान कर सकते हैं जो इंटरफ़ेस प्रकारों के मूल्यों का उपयोग/रिटर्न देता है। लेकिन यदि मान "गायब" है, तो स्पष्ट रूप से 'nil' वापस करें। – icza

2

चलिए एक सूचक के रूप में एक इंटरफेस के बारे में सोचते हैं।

कहें कि आपके पास एक सूचक a है और यह शून्य है, कुछ भी इंगित नहीं करता है।

var a *int // nil 

तो फिर तुम एक सूचक b है और यह a की ओर इशारा करते है।

var b **int 
b = &a // not nil 

देखें क्या हुआ? b एक पॉइंटर को इंगित करता है जो कुछ भी इंगित करता है। तो यदि यह श्रृंखला के अंत में एक शून्य सूचक है, b कुछ इंगित करता है - यह शून्य नहीं है।

आप इस प्रक्रिया को 'स्मृति में झांक सकते हैं तो यह इस प्रकार दिखाई देंगे:

address | name | value 
1000000 | a | 0 
2000000 | b | 1000000 

देखते हैं? a पता 0 पर इंगित कर रहा है (जिसका अर्थ है कि यह nil है), और ba (1000000) के पते पर इंगित कर रहा है।

यह इंटरफेस पर लागू होता है (सिवाय इसके कि वे in memory थोड़ा अलग दिखते हैं)।

एक सूचक की तरह, एक शून्य सूचक की ओर इशारा करते एक अंतरफलक ही नहीं के बराबर नहीं होगा।

यहां, अपने लिए how this works with pointers और how it works with interfaces देखें।

संबंधित मुद्दे