reference - रस्ट के सटीक ऑटो-डेरीफेरिंग नियम क्या हैं?



dereference formal-semantics (1)

मैं जंग के साथ सीख रहा हूं / प्रयोग कर रहा हूं, और इस भाषा में मुझे जो भी लालित्य मिल रहा है, उसमें एक खासियत यह है कि यह मुझे चकित करता है और पूरी तरह से बाहर लगता है।

विधि कॉल करते समय जंग स्वचालित रूप से बिंदुओं को रोकती है। मैंने सटीक व्यवहार निर्धारित करने के लिए कुछ परीक्षण किए:

struct X { val: i32 }
impl std::ops::Deref for X {
    type Target = i32;
    fn deref(&self) -> &i32 { &self.val }
}


trait            M                   { fn m(self); }
impl             M for i32           { fn m(self) { println!("i32::m()"); } }
impl             M for X             { fn m(self) { println!("X::m()"); } }
impl<'a>         M for &'a X         { fn m(self) { println!("&X::m()"); } }
impl<'a, 'b>     M for &'a &'b X     { fn m(self) { println!("&&X::m()"); } }
impl<'a, 'b, 'c> M for &'a &'b &'c X { fn m(self) { println!("&&&X::m()"); } }

trait            RefM                   { fn refm(&self); }
impl             RefM for i32           { fn refm(&self) { println!("i32::refm()"); } }
impl             RefM for X             { fn refm(&self) { println!("X::refm()"); } }
impl<'a>         RefM for &'a X         { fn refm(&self) { println!("&X::refm()"); } }
impl<'a, 'b>     RefM for &'a &'b X     { fn refm(&self) { println!("&&X::refm()"); } }
impl<'a, 'b, 'c> RefM for &'a &'b &'c X { fn refm(&self) { println!("&&&X::refm()"); } }

struct Y { val: i32 }
impl std::ops::Deref for Y {
    type Target = i32;
    fn deref(&self) -> &i32 { &self.val }
}

struct Z { val: Y }
impl std::ops::Deref for Z {
    type Target = Y;
    fn deref(&self) -> &Y { &self.val }
}

struct A;
impl std::marker::Copy for A {}
impl             M for             A { fn m(self) { println!("A::m()"); } }
impl<'a, 'b, 'c> M for &'a &'b &'c A { fn m(self) { println!("&&&A::m()"); } }
impl             RefM for             A { fn refm(&self) { println!("A::refm()"); } }
impl<'a, 'b, 'c> RefM for &'a &'b &'c A { fn refm(&self) { println!("&&&A::refm()"); } }

fn main() {
    // I'll use @ to denote left side of the dot operator
    (*X{val:42}).m();        // i32::refm() , self == @
    X{val:42}.m();           // X::m()      , self == @
    (&X{val:42}).m();        // &X::m()     , self == @
    (&&X{val:42}).m();       // &&X::m()    , self == @
    (&&&X{val:42}).m();      // &&&X:m()    , self == @
    (&&&&X{val:42}).m();     // &&&X::m()   , self == *@
    (&&&&&X{val:42}).m();    // &&&X::m()   , self == **@

    (*X{val:42}).refm();     // i32::refm() , self == @
    X{val:42}.refm();        // X::refm()   , self == @
    (&X{val:42}).refm();     // X::refm()   , self == *@
    (&&X{val:42}).refm();    // &X::refm()  , self == *@
    (&&&X{val:42}).refm();   // &&X::refm() , self == *@
    (&&&&X{val:42}).refm();  // &&&X::refm(), self == *@
    (&&&&&X{val:42}).refm(); // &&&X::refm(), self == **@

    Y{val:42}.refm();        // i32::refm() , self == *@
    Z{val:Y{val:42}}.refm(); // i32::refm() , self == **@

    A.m();                   // A::m()      , self == @
    // without the Copy trait, (&A).m() would be a compilation error:
    // cannot move out of borrowed content
    (&A).m();                // A::m()      , self == *@
    (&&A).m();               // &&&A::m()   , self == &@
    (&&&A).m();              // &&&A::m()   , self == @
    A.refm();                // A::refm()   , self == @
    (&A).refm();             // A::refm()   , self == *@
    (&&A).refm();            // A::refm()   , self == **@
    (&&&A).refm();           // &&&A::refm(), self == @
}

तो, ऐसा लगता है कि, कम या ज्यादा:

  • कंपाइलर एक विधि को लागू करने के लिए आवश्यक रूप से जितने भी डिरेक्शन ऑपरेटर डालेंगे।
  • संकलक, जब हल करने के तरीके &self (कॉल-बाय-रेफरेंस) का उपयोग करके घोषित किया जाता है:
    • पहले self एक ही अनुमापन के लिए कॉल करने की कोशिश करता है
    • फिर self के सटीक प्रकार के लिए कॉल करने का प्रयास करता है
    • फिर, एक मैच के लिए आवश्यक के रूप में कई dereference ऑपरेटरों को डालने की कोशिश करता है
  • टाइप T लिए self (कॉल-बाय-वैल्यू) का उपयोग करने की घोषणा की गई पद्धतियां ऐसा मानती हैं कि उन्हें टाइप &T लिए &self (कॉल-बाय-रेफरेंस) का उपयोग करते हुए घोषित किया गया था &self डॉट ऑपरेटर के बाईं ओर जो कुछ भी है, उसके संदर्भ में कॉल किया गया है।
  • उपरोक्त नियमों को पहले कच्चे बिल्ट-इन Deref साथ करने की कोशिश की जाती है, और अगर कोई मेल नहीं है, तो Deref विशेषता के साथ अधिभार का उपयोग किया जाता है।

सटीक ऑटो-डेरीफेरिंग नियम क्या हैं? क्या कोई भी इस तरह के डिजाइन निर्णय के लिए कोई औपचारिक तर्क दे सकता है?

https://ffff65535.com


आपका छद्म कोड बहुत सही है। इस उदाहरण के लिए, मान लीजिए कि हमारे पास एक विधि foo.bar() जहां foo: T । मैं पूरी तरह से योग्य वाक्यविन्यास (FQS) का उपयोग करने जा रहा हूं, इस बारे में असंदिग्ध होना चाहिए कि किस प्रकार की विधि के साथ कॉल किया जा रहा है, जैसे कि A::bar(foo) या A::bar(&***foo) । मैं बस रैंडम कैपिटल लेटर्स का ढेर लिखने जा रहा हूं, हर एक में बस कुछ मनमानी टाइप / ट्रिट होती है, सिवाय T हमेशा ऑरिजनल वेरिएबल foo का टाइप होता है जिसे मेथड कहा जाता है।

एल्गोरिथ्म का मूल है:

  • प्रत्येक "dereference स्टेप" U (अर्थात, U = T सेट करें और फिर U = *T , ...)
    1. यदि वहाँ एक विधि bar जहाँ रिसीवर प्रकार (विधि में self का प्रकार) U बिल्कुल मेल खाता है, तो इसका उपयोग करें ( "मान विधि द्वारा" )
    2. अन्यथा, एक ऑटो-रेफरी (रिसीवर के &mut ले या म्यूट ) जोड़ें, और, अगर कुछ विधि के रिसीवर से मेल खाता है &U , इसका उपयोग करें ( एक "ऑटोरेफ़्ड विधि" )

विशेष रूप से, सब कुछ विधि के "रिसीवर प्रकार" पर विचार करता है, कि Self प्रकार का गुण, यानी impl ... for Foo { fn method(&self) {} } लिए विधि के मिलान के समय &Foo बारे में सोचता है, और fn method2(&mut self) मिलान करते समय &mut Foo बारे में सोचते होंगे।

यह एक त्रुटि है अगर भीतर के चरणों में कभी भी एकाधिक गुण विधियां मान्य हैं (अर्थात, केवल 1. या 2. में से प्रत्येक में शून्य या एक विशेषता विधियां मान्य हो सकती हैं, लेकिन प्रत्येक के लिए एक वैध हो सकती हैं: एक 1 से पहले लिया जाएगा), और निहित तरीकों से विशेषता वाले लोगों पर पूर्वता प्राप्त होती है। यह एक त्रुटि है अगर हम कुछ भी पाने के बिना लूप के अंत तक पहुंचते हैं जो मेल खाता है। पुनरावर्ती Deref कार्यान्वयन भी एक त्रुटि है, जो लूप को अनंत बनाते हैं (वे "पुनरावृत्ति सीमा" Deref )।

ये नियम ज्यादातर परिस्थितियों में क्या-क्या-क्या-क्या-क्या करते हैं, मुझे लगता है, हालांकि कुछ धारियों के लिए अस्पष्ट FQS फॉर्म लिखने की क्षमता बहुत उपयोगी है, और मैक्रो जनरेटेड कोड के लिए समझदार त्रुटि संदेशों के लिए।

केवल एक ऑटो-संदर्भ जोड़ा जाता है क्योंकि

  • अगर कोई बाध्य नहीं था, तो चीजें खराब / धीमी हो जाती हैं, क्योंकि हर प्रकार के संदर्भों की एक मनमानी संख्या हो सकती है
  • एक संदर्भ &foo लेने से &foo एक मजबूत संबंध बना रहता है (यह खुद को foo का पता है), लेकिन इसे खोने के लिए और अधिक शुरू होता है: &&foo पर कुछ अस्थायी चर का पता है जो स्टोर &foo

उदाहरण

मान लें कि हमारे पास foo.refm() , अगर foo में टाइप है:

  • X , फिर हम U = X शुरू करते हैं, refm में रिसीवर प्रकार &... , इसलिए चरण 1 मेल नहीं खाता है, ऑटो-रेफ लेने से हमें &X , और यह मेल खाता है ( Self = X ), इसलिए कॉल RefM::refm(&foo)
  • &X , U = &X शुरू होता है, जो पहले चरण में ( Self = X ) मेल खाता है, और इसलिए कॉल RefM::refm(foo)
  • &&&&&X , यह या तो चरण से मेल नहीं खाता (विशेषता के लिए लागू नहीं किया गया है &&&&X या &&&&&X ), इसलिए हम एक बार U = &&&&X , जो 1 से मेल खाता है ( Self = &&&X ) और कॉल RefM::refm(*foo)
  • Z , या तो चरण से मेल नहीं खाता है, इसलिए इसे Y से प्राप्त करने के लिए एक बार डीरएफ़र्ड किया जाता है, जो मेल नहीं खाता है, इसलिए X को प्राप्त करने के लिए इसे फिर से डिरेल किया जाता है, जो 1 से मेल नहीं खाता है, लेकिन ऑटोरेफ़िंग के बाद मेल खाता है, इसलिए कॉल है RefM::refm(&**foo)
  • &&A , 1. मेल नहीं खाता है और न ही करता है। 2. चूंकि विशेषता 1 &A (1) या &&A (2 के लिए) के लिए लागू नहीं होती है, इसलिए यह &A से मेल खाता है, जो 1. से मेल खाता है, Self = A

मान लें कि हमारे पास foo.m() , और यह कि A की Copy नहीं है, अगर foo में टाइप है:

  • A , फिर U = A सीधे self से मेल खाता है इसलिए कॉल M::m(foo) Self = A
  • &A , फिर 1. मेल नहीं खाता है, और न ही 2. (और न ही &A और न ही &&A लागू करता है), इसलिए इसे A लिए डीरएफ़र्ड किया जाता है, जो मेल खाता है, लेकिन M::m(*foo) को A द्वारा मान लेने की आवश्यकता होती है और इसलिए foo बाहर जा रहा है, इसलिए त्रुटि।
  • &&A , 1. मेल नहीं खाता, लेकिन autorefing देता है &&&A , जो मेल खाता है, इसलिए कॉल M::m(&foo) साथ Self = &&&A

(यह उत्तर कोड पर आधारित है, और यथोचित रूप से (थोड़ा पुराना) README के ​​करीब है । कंपाइलर / भाषा के इस हिस्से के मुख्य लेखक निको मत्सकिस ने भी इस उत्तर पर गौर किया।)





rust