Was sind Rost - genaue auto-Dereferenzierung Regeln?

Ich bin lernen/Experimentieren mit Rost, und in all der Eleganz, die ich finde, in dieser Sprache, es ist eine Besonderheit, dass verwirrt mich und erscheint völlig deplatziert.

Rost automatisch dereferenziert Zeiger, wenn man Methodenaufrufe. Ich machte einige tests, um zu bestimmen, das genaue Verhalten:

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 == @
}

So, es scheint, dass mehr oder weniger:

  • Der compiler fügt so viele dereferenzieren Betreiber wie notwendig, um eine Methode aufzurufen.
  • Der compiler beim auflösen von Methoden deklariert, mit &self (call-by-reference):
    • Ersten mal versucht, den Aufruf für eine einzelne dereferenzieren von self
    • Dann versucht der Aufruf für die genaue Art der self
    • Dann versucht Sie das einfügen so viele dereferenzieren Betreiber als notwendig für ein Spiel
  • Methoden deklariert, mit self (call-by-value) für Typ T Verhalten, als wenn Sie deklariert wurden, mit &self (call-by-reference) für Typ &T genannt und der Verweis auf das, was ist auf der linken Seite des Punkt-operators.
  • Die oben genannten Regeln sind zuerst versucht, mit raw-built-in-array-Dereferenzierung, und wenn es keine übereinstimmung, wird die überlast mit Deref Merkmal verwendet wird.

Was sind die genauen automatische Dereferenzierung Regeln? Kann jemand geben eine formale Begründung für eine solche design-Entscheidung?

Ich habe cross-gepostet, dies zu Rost subreddit in der Hoffnung ein paar gute Antworten!
Für zusätzlichen Spaß, versuchen Sie, wiederholen Sie das experiment im Bereich der Generika und die Ergebnisse vergleichen.

InformationsquelleAutor kFYatek | 2015-02-14

Schreibe einen Kommentar