मैं रस्ट के साथ सीख रहा हूं / प्रयोग कर रहा हूं, और इस भाषा में मुझे जो भी लालित्य मिल रहा है, उसमें एक ख़ासियत यह है कि मुझे चकित करता है और पूरी तरह से बाहर लगता है।
विधि कॉल करते समय जंग स्वचालित रूप से बिंदुओं को रोकती है। मैंने सटीक व्यवहार निर्धारित करने के लिए कुछ परीक्षण किए:
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
विशेषता के साथ अधिभार का उपयोग किया जाता है।
सटीक ऑटो-डेरीफेरिंग नियम क्या हैं? क्या कोई भी इस तरह के डिजाइन निर्णय के लिए कोई औपचारिक तर्क दे सकता है?