আমি মরিচাটির সাথে শিখছি / পরীক্ষা নিরীক্ষা করছি এবং এই ভাষায় আমি যে কমনীয়তা পেয়েছি সেখানে একটি বিশেষত্ব আছে যা আমাকে বিস্মিত করে এবং পুরোপুরি জায়গা থেকে দূরে বলে মনে হয়।
মেথড কল করার সময় মরিচা স্বয়ংক্রিয়ভাবে পয়েন্টারগুলিকে ডিফেরেন্ট করে। সঠিক আচরণ নির্ধারণের জন্য আমি কিছু পরীক্ষা করেছি:
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
- তারপরে, কোনও ম্যাচের জন্য প্রয়োজনীয় যতগুলি ডিরিফারেন্স অপারেটর inোকানোর চেষ্টা করে
- প্রথমে এর একক সম্মানের জন্য আহ্বান জানাতে চেষ্টা করুন
self
প্রকারের জন্য (কল-বাই-মান) ব্যবহার করে ঘোষিত পদ্ধতিগুলিT
এমনভাবে আচরণ করে যেন এগুলি টাইপের জন্য&self
(কল-বাই-রেফারেন্স) ব্যবহার করে ঘোষিত হয়েছিল&T
এবং বিন্দু অপারেটরের বাম দিকে যা আছে তার রেফারেন্সটিতে কল করা হয়েছে।- উপরোক্ত নিয়মগুলি প্রথমে কাঁচা বিল্ট-ইন ডেরেফারেন্স দিয়ে চেষ্টা করা হয় এবং যদি কোনও মিল না থাকে তবে
Deref
বৈশিষ্ট্যযুক্ত ওভারলোড ব্যবহার করা হয়।
সঠিক স্বতঃ-নির্ধারণের বিধিগুলি কী কী? এমন ডিজাইনের সিদ্ধান্তের জন্য কেউ কি কোনও আনুষ্ঠানিক যুক্তি দিতে পারেন?