2015-06-27 3 views
12

Zuerst entlehnt ist, lassen Sie den Code sprechen:kann nicht `self.x` als unveränderlich leihen, weil` * self` auch als veränderbare

#[derive(Debug)] 
struct Bar; 

#[derive(Debug)] 
struct Qux { 
    baz: bool 
} 

#[derive(Debug)] 
struct Foo { 
    bars: Vec<Bar>, 
    qux: Qux, 
} 

impl Foo { 
    fn get_qux(&mut self) -> &mut Qux { 
     &mut self.qux 
    } 

    fn run(&mut self) { 
     // 1. Fails: 
     let mut qux = self.get_qux(); 

     // 2. Works: 
     // let mut qux = &mut Qux { baz: false }; 

     // 3. Works: 
     // let mut qux = &mut self.qux; 

     let qux_mut = &mut qux; 
     qux_mut.baz = true; 

     for bar in &self.bars { 
      println!("{:?}", bar); 
     } 
    } 
} 

fn main() { 
    println!("Hello, world!"); 

    let mut foo = Foo { bars: vec!(), qux: Qux { baz: false } }; 
    foo.run(); 
} 

Diese Fehler:

error[E0502]: cannot borrow `self.bars` as immutable because `*self` is also borrowed as mutable 
    --> src/main.rs:33:21 
    | 
22 |   let mut qux = self.get_qux(); 
    |      ---- mutable borrow occurs here 
... 
33 |   for bar in &self.bars { 
    |      ^^^^^^^^^ immutable borrow occurs here 
... 
36 |  } 
    |  - mutable borrow ends here 

Wenn ich Kommentar- entweder 2. oder 3., warum kompiliert es gerade gut? Die aufgerufene Funktion in 1. unterscheidet sich nicht wesentlich von 2. oder 3.. Warum also kann 1. nicht kompiliert werden?

Obwohl es many similar titled questions gibt, konnte ich dies nicht eindeutig als ein Betrüger identifizieren (abgesehen davon, dass die Fehlermeldung die gleiche ist), möglicherweise wegen meines mangelnden Verständnisses für das Eigentums-/Entleihsystem in Rust.

Antwort

8

In Rust stoppt der Compiler an der Funktionsaufrufgrenze, wenn generische Parameter ausgewertet werden, die generische Lebensdauerparameter enthalten. In Ihrem Fall 1, rufen Sie eine Methode:

fn get_qux(&mut self) -> &mut Qux { 
    &mut self.qux 
} 

Diese Funktion zeigt an, dass alle von self wird mutably ausgeliehen werden, und dass die zurückgegebene Referenz wird solange self Willen leben. Während dieser Zeit können keine anderen (veränderlichen oder nicht) Anleihen von sich selbst oder seinen Komponenten gemacht werden.

In Ihrem zweiten Fall bilden Sie eine komplett neue Qux, die keine Anbindung an Ihre Struktur hat. Es ist kein wirklich gutes Beispiel, weil es eine ganz andere Bedeutung hat. Wenn dieser Fall für Sie funktioniert, sollten Sie das tun. Sie werden jedoch nicht dasselbe wie Fall 1 ändern.

Im dritten Fall vermeiden Sie den Funktionsaufruf. Das bedeutet, dass der Compiler ein bisschen mehr Informationen darüber hat, was genau ausgeliehen ist. Insbesondere kann es sehen, dass self.qux überhaupt nicht mit self.bars interagieren, so dass es keinen Fehler gibt.

Sie können Ihre ursprüngliche Beispiel Arbeit durch Hinzufügen eines neuen Bereichs machen:

fn run(&mut self) { 
    { 
     let mut qux = self.get_qux(); 
     let qux_mut = &mut qux; 
     qux_mut.baz = true; 
    } 

    for bar in &self.bars { 
     println!("{:?}", bar); 
    } 
} 

Hier wird der künstliche Umfang klar definiert, wo die wandelbar borrow endet. Sobald der Kredit vorbei ist, können andere Gegenstände neue Kredite aufnehmen.

Wenn benötigen Sie qux innerhalb der Schleife zu ändern, dann werden Sie das dritte Muster folgen erforderlich:

let mut qux = &mut self.qux; 

for bar in &self.bars { 
    qux.baz = ! qux.baz; 
    println!("{:?}", bar); 
} 

Oder die einfacheren:

for bar in &self.bars { 
    self.qux.baz = ! self.qux.baz; 
    println!("{:?}", bar); 
} 

Viele Male, können Sie Ihren Code Refactoring um neue Strukturen zu erzeugen, die Informationen enthalten und eine nette Mutationsgrenze einkapseln, um Code wie diesen zu machen.

+3

Vielen Dank für Ihre gründliche Antwort. Ich frage mich jedoch, wie es dann möglich ist, 'qux' innerhalb der for-Schleife zu verwenden, wenn ich müsste? –

+0

@FelixSchlitter aktualisiert. – Shepmaster

+1

Ich verstehe. Aber nehmen wir an, dass die Funktion eine zusätzliche Logik enthielt, z. Es brauchte eine ID und holte "Qux" von einem Vec von Quxes zurück, dann musste ich diese Logik überall hinzufügen. Vielleicht muss ich auf ein Makro zurückgreifen? ...Ich weiß, dass die 'HashMap'- und' Vec'-Typen eine 'get_mut'-Methode haben, vielleicht gibt es auch etwas zu lernen von ihrer Implementierung. Ich werde einen weiteren Tauchgang machen müssen. –

Verwandte Themen