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


182

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

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

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 M for &X    { fn m(self) { println!("&X::m()");   } }
impl M for &&X   { fn m(self) { println!("&&X::m()");  } }
impl M for &&&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 RefM for &X   { fn refm(&self) { println!("&X::refm()");   } }
impl RefM for &&X  { fn refm(&self) { println!("&&X::refm()");  } }
impl RefM for &&&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 }
}


#[derive(Clone, Copy)]
struct A;

impl M for    A { fn m(self) { println!("A::m()");    } }
impl M for &&&A { fn m(self) { println!("&&&A::m()"); } }

impl RefM for    A { fn refm(&self) { println!("A::refm()");    } }
impl RefM for &&&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::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}).m();    // &&&X::m()   , Self == **@
    println!("-------------------------");

    (*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 == **@
    println!("-------------------------");

    Y{val:42}.refm();        // i32::refm() , Self == *@
    Z{val:Y{val:42}}.refm(); // i32::refm() , Self == **@
    println!("-------------------------");

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

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


1
मैंने कुछ अच्छे उत्तर पाने की उम्मीद में इसे रस्ट सबरडिट पर क्रॉस-पोस्ट किया है!
Shepmaster

अतिरिक्त मज़ा के लिए जेनेरिक में प्रयोग को दोहराने और परिणामों की तुलना करने का प्रयास करें।
user2665887

जवाबों:


137

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

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

  • प्रत्येक "डीरेफेरेंस स्टेप" के लिए 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क्रियान्वयन होना भी एक त्रुटि है , जो लूप को अनंत बना देता है (वे "पुनरावृत्ति सीमा" को मार देंगे)।

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

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

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

उदाहरण

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

  • X, तो हम साथ शुरू U = X, refmरिसीवर प्रकार है &...तो चरण 1 से मेल नहीं खाता, ले जा रहा एक ऑटो-रेफरी हमें देता है, &Xऔर इस मैच (के साथ करता है Self = Xतो कॉल है),RefM::refm(&foo)
  • &X, के साथ शुरू होता है U = &X, जो &selfपहले चरण (साथ 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. चूंकि विशेषता &A1 के लिए (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. मेल नहीं खाता है, लेकिन ऑटोरेफ़िंग देता है &&&A, जो मेल खाता है, इसलिए कॉल के M::m(&foo)साथ है Self = &&&A

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


15
यह उत्तर संपूर्ण और विस्तृत लगता है लेकिन मुझे लगता है कि इसमें नियमों की कमी और सुलभ गर्मियों का अभाव है। शेमकेस्टर की इस टिप्पणी में एक ऐसी गर्मी दी गई है : "यह [deref एल्गोरिथ्म] यथासंभव कई बार ( &&String-> &String-> String-> str) deref होगा और फिर अधिकतम एक बार ( str-> &str) संदर्भ "।
Lii

(मुझे नहीं पता कि यह स्पष्टीकरण कितना सही और पूर्ण है।)
Lii

1
ऑटो डीरेफ्रेंसिंग किन मामलों में होती है? क्या इसका उपयोग केवल रिसीवर के लिए मेथड कॉल के लिए किया जाता है? फ़ील्ड एक्सेस के लिए भी? दाएं हाथ से असाइनमेंट? बाएं हाथ के किनारे? समारोह मापदंडों? मूल्य मान लौटाएँ?
Lii

1
नोट: वर्तमान में, नामांकित व्यक्ति के पास इस जवाब से जानकारी चुराने के लिए TODO नोट है और इसे static.rust-lang.org/doc/master/nomicon/dot-operator.html
SamB

1
क्या इस (या (बी) के बाद (या) इस एल्गोरिथम के हर चरण में (या) कुछ और करने की कोशिश की गई है इससे पहले (बी) के साथ जबरदस्ती की कोशिश की गई है?

8

रस्ट संदर्भ में विधि कॉल अभिव्यक्ति के बारे में एक अध्याय है । मैंने सबसे महत्वपूर्ण हिस्सा नीचे कॉपी किया। अनुस्मारक: हम एक अभिव्यक्ति के बारे में बात कर रहे हैं recv.m(), जहां recvनीचे "रिसीवर अभिव्यक्ति" कहा जाता है।

पहला कदम उम्मीदवार रिसीवर प्रकारों की एक सूची बनाना है। रिसीवर एक्सप्रेशन के प्रकार को बार-बार डीरेफरेंस करके इन्हें प्राप्त करें, सूची में सामने आए प्रत्येक प्रकार को जोड़ते हुए, फिर अंत में एक अनियंत्रित ज़बरदस्ती का प्रयास करें, और यदि सफल हो तो परिणाम प्रकार को जोड़ दें। फिर, प्रत्येक उम्मीदवार के लिए, सूची में Tजोड़ें &Tऔर &mut Tउसके तुरंत बाद T

उदाहरण के लिए, अगर रिसीवर प्रकार है Box<[i32;2]>, तो उम्मीदवार प्रकार किया जाएगा Box<[i32;2]>, &Box<[i32;2]>, &mut Box<[i32;2]>, [i32; 2](अपसंदर्भन द्वारा), &[i32; 2], &mut [i32; 2], [i32](अनवधि बलात्कार द्वारा), &[i32]और अंत में &mut [i32]

फिर, प्रत्येक उम्मीदवार प्रकार के लिए T, निम्न स्थानों में उस प्रकार के रिसीवर के साथ एक दृश्य विधि की खोज करें:

  1. Tनिहित विधियों (तरीकों T[।] पर सीधे लागू )।
  2. इसके द्वारा कार्यान्वित दृश्यमान विशेषता द्वारा प्रदान की गई विधियाँ T। [...]

( [ : ] के बारे में ध्यान दें : मुझे वास्तव में लगता है कि यह वाक्यांश गलत है। मैंने एक मुद्दा खोला है । चलिए इस वाक्य को कोष्ठक में अनदेखा करते हैं।)


चलिए अपने कोड से कुछ उदाहरणों के बारे में विस्तार से जानते हैं! आपके उदाहरणों के लिए, हम "अनियोजित ज़बरदस्ती" और "निहित तरीकों" के बारे में भाग को अनदेखा कर सकते हैं।

(*X{val:42}).m(): रिसीवर अभिव्यक्ति का प्रकार है i32। हम ये कदम उठाते हैं:

  • उम्मीदवार रिसीवर प्रकारों की सूची बनाना:
    • i32 डीरेफर नहीं किया जा सकता है, इसलिए हम पहले से ही चरण 1 के साथ कर रहे हैं। सूची: [i32]
    • अगला, हम जोड़ते हैं &i32और &mut i32। सूची:[i32, &i32, &mut i32]
  • प्रत्येक उम्मीदवार रिसीवर प्रकार के लिए तरीकों की खोज:
    • हम पाते हैं <i32 as M>::mकि रिसीवर के प्रकार कौन से हैं i32। तो हम पहले से ही कर रहे हैं।


अब तक इतना आसान है। अब एक और अधिक कठिन उदाहरण लेते हैं (&&A).m():। रिसीवर अभिव्यक्ति का प्रकार है &&A। हम ये कदम उठाते हैं:

  • उम्मीदवार रिसीवर प्रकारों की सूची बनाना:
    • &&Aके लिए dereferenced जा सकता है &A, इसलिए हम इसे सूची में जोड़ते हैं। &Aहम फिर Aसे सूची में जोड़ सकते हैं । Aहम नहीं रोक सकते, इसलिए हम रोकते हैं। सूची:[&&A, &A, A]
    • अगला, Tसूची में प्रत्येक प्रकार के लिए, हम जोड़ते हैं &Tऔर &mut Tतुरंत बाद T। सूची:[&&A, &&&A, &mut &&A, &A, &&A, &mut &A, A, &A, &mut A]
  • प्रत्येक उम्मीदवार रिसीवर प्रकार के लिए तरीकों की खोज:
    • रिसीवर प्रकार के साथ कोई विधि नहीं है &&A, इसलिए हम सूची में अगले प्रकार पर जाते हैं।
    • हमें वह विधि मिलती है <&&&A as M>::mजिसमें वास्तव में रिसीवर का प्रकार होता है &&&A। तो हम कर रहे हैं।

यहां आपके सभी उदाहरणों के लिए उम्मीदवार रिसीवर सूची दी गई है। जिस प्रकार से संलग्न है ⟪x⟫वह वह है जो "जीता" है, अर्थात पहला प्रकार जिसके लिए एक फिटिंग विधि मिल सकती है। यह भी याद रखें कि सूची में पहला प्रकार हमेशा रिसीवर अभिव्यक्ति का प्रकार होता है। अंत में, मैंने सूची को तीन की पंक्तियों में स्वरूपित किया, लेकिन यह सिर्फ स्वरूपण है: यह सूची एक सपाट सूची है।

  • (*X{val:42}).m()<i32 as M>::m
    [i32, &i32, &mut i32]
  • X{val:42}.m()<X as M>::m
    [⟪X⟫, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&X{val:42}).m()<&X as M>::m
    [&X⟫, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&X{val:42}).m()<&&X as M>::m
    [&&X⟫, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&X{val:42}).m()<&&&X as M>::m
    [&&&X⟫, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&&X{val:42}).m()<&&&X as M>::m
    [&&&&X, &&&&&X, &mut &&&&X,&&&X⟫, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&&&X{val:42}).m()<&&&X as M>::m
    [&&&&&X, &&&&&&X, &mut &&&&&X, 
     &&&&X, &&&&&X, &mut &&&&X,&&&X⟫, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]


  • (*X{val:42}).refm()<i32 as RefM>::refm
    [i32,&i32, &mut i32]
  • X{val:42}.refm()<X as RefM>::refm
    [X,&X⟫, &mut X, 
     i32, &i32, &mut i32]
  • (&X{val:42}).refm()<X as RefM>::refm
    [&X⟫, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&X{val:42}).refm()<&X as RefM>::refm
    [&&X⟫, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&X{val:42}).refm()<&&X as RefM>::refm
    [&&&X⟫, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&&X{val:42}).refm()<&&&X as RefM>::refm
    [&&&&X⟫, &&&&&X, &mut &&&&X, 
     &&&X, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]
  • (&&&&&X{val:42}).refm()<&&&X as RefM>::refm
    [&&&&&X, &&&&&&X, &mut &&&&&X,&&&&X⟫, &&&&&X, &mut &&&&X, 
     &&&X, &&&&X, &mut &&&X, 
     &&X, &&&X, &mut &&X, 
     &X, &&X, &mut &X, 
     X, &X, &mut X, 
     i32, &i32, &mut i32]


  • Y{val:42}.refm()<i32 as RefM>::refm
    [Y, &Y, &mut Y,
     i32,&i32, &mut i32]
  • Z{val:Y{val:42}}.refm()<i32 as RefM>::refm
    [Z, &Z, &mut Z,
     Y, &Y, &mut Y,
     i32,&i32, &mut i32]


  • A.m()<A as M>::m
    [⟪A⟫, &A, &mut A]
  • (&A).m()<A as M>::m
    [&A, &&A, &mut &A,
     ⟪A⟫, &A, &mut A]
  • (&&A).m()<&&&A as M>::m
    [&&A,&&&A⟫, &mut &&A,
     &A, &&A, &mut &A,
     A, &A, &mut A]
  • (&&&A).m()<&&&A as M>::m
    [&&&A⟫, &&&&A, &mut &&&A,
     &&A, &&&A, &mut &&A,
     &A, &&A, &mut &A,
     A, &A, &mut A]
  • A.refm()<A as RefM>::refm
    [A,&A⟫, &mut A]
  • (&A).refm()<A as RefM>::refm
    [&A⟫, &&A, &mut &A,
     A, &A, &mut A]
  • (&&A).refm()<A as RefM>::refm
    [&&A, &&&A, &mut &&A,&A⟫, &&A, &mut &A,
     A, &A, &mut A]
  • (&&&A).refm()<&&&A as RefM>::refm
    [&&&A,&&&&A⟫, &mut &&&A,
     &&A, &&&A, &mut &&A,
     &A, &&A, &mut &A,
     A, &A, &mut A]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.