2016-07-28 22 views
4

Bei der Erstellung einer FooBuilder, möchte ich eine &mut Bar bereitstellen. Wenn ich Foo baue, möchte ich eine &Bar bereitstellen und Foo sollte &self Methoden von Bar aufrufen können. Mit anderen Worten, der veränderbare Kredit sollte nur während der Lebenszeit von FooBuilder existieren.Wie gebe ich einen veränderbaren Verweis auf einen Builder, aber nur einen unveränderlichen Verweis auf das erstellte Objekt?

struct FooBuilder<'a> { 
    bar: &'a mut Bar, 
} 
impl<'a> FooBuilder<'a> { 
    fn new(bar: &'a mut Bar) -> Self { 
     FooBuilder { bar: bar } 
    } 
    fn build(&'a self) -> Foo<'a> { 
     Foo { bar: &self.bar } 
    } 
} 

struct Foo<'a> { 
    bar: &'a Bar, 
} 

struct Bar; 
impl Bar { 
    fn bar(&self) {} 
} 

fn main() { 
    let mut bar = Bar; 
    let foo = FooBuilder::new(&mut bar).build(); 
    bar.bar(); 
} 

Dieser Code hat den Fehler:

error: borrowed value does not live long enough 
    --> <anon>:24:15 
    | 
24 |  let foo = FooBuilder::new(&mut bar).build(); 
    |    ^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long  enough 
    | 
note: reference must be valid for the block suffix following  statement 1 at 24:48... 
    --> <anon>:24:49 
    | 
24 |  let foo = FooBuilder::new(&mut bar).build(); 
    |            ^
note: ...but borrowed value is only valid for the statement at 24:4 
    --> <anon>:24:5 
    | 
24 |  let foo = FooBuilder::new(&mut bar).build(); 
    |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
help: consider using a `let` binding to increase its lifetime 
    --> <anon>:24:5 
    | 
24 |  let foo = FooBuilder::new(&mut bar).build(); 
    |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

error[E0502]: cannot borrow `bar` as immutable because it is also  borrowed as mutable 
    --> <anon>:25:5 
    | 
24 |  let foo = FooBuilder::new(&mut bar).build(); 
    |         --- mutable borrow occurs  here 
25 |  bar.bar(); 
    |  ^^^ immutable borrow occurs here 
26 | } 
    | - mutable borrow ends here 

error: aborting due to 2 previous errors 
+0

Was genau ist Ihre Frage? – antoyo

+3

Es ist nicht die beste Etikette, Ihre Frage so zu modifizieren, dass bestehende Antworten ungültig werden. – Shepmaster

+0

Ich glaube nicht, dass das möglich ist, sorry. Sie können einen veränderbaren Verweis nicht auf einen freigegebenen Verweis zurückstufen, um die gemeinsame Lebensdauer wiederherzustellen. – Veedrac

Antwort

1

Sie etwas tun können, ähnlich, wenn Sie nicht bar in einer Rc Einwickeln nichts ausmacht. Der Trick ist, dass, wenn es nur einen Rc Verweis gibt, Sie einen &mut Verweis auf den Inhalt erhalten können. Das ist irgendwie rückwärts; anstatt &mut zu & zur Kompilierzeit herunterzurechnen, verwendet es Laufzeitinformationen (Referenzzählungen), um von einer unveränderlichen Referenz auf veränderbar zu "upgraden".

use std::rc::Rc; 

struct FooBuilder<'a> { 
    bar: &'a mut Rc<Bar>, 
} 
impl<'a> FooBuilder<'a> { 
    fn new(bar: &'a mut Rc<Bar>) -> Self { 
     FooBuilder { bar: bar } 
    } 
    fn f(mut self) -> Self { 
     Rc::get_mut(self.bar).unwrap().mut_method(); 
     self 
    } 
    fn build(&'a self) -> Foo { 
     Foo { bar: self.bar.clone() } 
    } 
} 

struct Foo { 
    bar: Rc<Bar>, 
} 

struct Bar; 
impl Bar { 
    fn bar(&self) {} 
    fn mut_method(&mut self) {} 
} 

fn main() { 
    let mut bar = Rc::new(Bar); 
    let foo = FooBuilder::new(&mut bar).f().build(); 
    bar.bar(); 
} 

Play link

Sobald die Foo hat mit einem Rc Klon konstruiert worden ist, ist es mehr als eine Referenz und ein später Versuch, eine mut Referenz Panik würde zu bekommen (oder zumindest Rückkehr None von Rc::get_mut()).

Dies bedeutet, dass Sie dies nur einmal tun können; Wenn Sie eine zweite eine zweite Foo aus der gleichen bar erstellen möchten, wird es nicht funktionieren, wie Sie keine anderen Referenzen sind, wenn Sie eine &mut T haben.

Dies ist ein wenig ungeschickt, und es gibt wahrscheinlich bessere Möglichkeiten, das tatsächliche Problem zu lösen, abhängig von den Umständen.

+1

Wenn Sie dynamisch nachprüfen, ist ein 'RefCell' viel weniger Schwergewicht. https://play.rust-lang.org/?gist=88a746b54a6cd6e6ede1818c8ce136d2&version=stable&backtrace=0 – Veedrac

+0

Das stimmt, aber gibt nicht den "convert to immutable" -Teil. Ich sage nicht, dass das unbedingt erwünscht ist! –

2

Der erste Schritt besteht darin, build zu beheben.

Um &mut T in eine &T Sie müssen zu verwandeln verbrauchen&mut T (sonst würden Sie Aliasing und Veränderlichkeit haben). Das bedeutet:

  • den Erbauer raubend, nicht einen Verweis darauf
  • Bestehen der wandelbaren Referenz genommen wird, nicht einen Verweis auf sie unter

Kurz gesagt, gehen Sie von:

fn build(&'a self) -> Foo<'a> { 
    Foo { bar: &self.bar } 
} 

zu:

fn build(self) -> Foo<'a> { 
    Foo { bar: self.bar } 
} 

Dies läßt Dich mit einem einzigen Fehler:

error: cannot borrow `bar` as immutable because it is also borrowed as mutable [--explain E0502] 
    --> <anon>:25:5 
24 |>  let foo = FooBuilder::new(&mut bar).build(); 
    |>         --- mutable borrow occurs here 
25 |>  bar.bar(); 
    |>  ^^^ immutable borrow occurs here 
26 |>  //foo.bar.bar(); 
27 |> } 
    |> - mutable borrow ends here 

Soweit der Compiler aus den Methodensignaturen sehen kann, ist bar entlehnt mutably und daher nicht direkt verwendet werden kann. Der Kredit verlängert sich, bis foo fallengelassen wird.

Die Lösung ist sehr einfach: Anstatt bar direkt zu verwenden, verwenden Sie bar von seiner Referenz in foo. Oder um klar zu stellen, dass der Umfang wichtig ist:

fn main() { 
    let mut bar = Bar; 
    { 
     let foo = FooBuilder::new(&mut bar).build(); 
     // `bar` currently borrow (mutably) by `foo`, cannot use it directly 
     foo.bar.bar(); 
    } 
    // `bar` no longer borrowed, use at your heart's content 
    bar.bar(); 
} 
+1

Ich denke, du hast die Frage missverstanden; Mein Verständnis ist, dass Ihr letzter Schritt - "foo" vor der Verwendung von "bar" zu verwerfen - gegen die Wünsche des OP ist. OP möchte, dass "foo" und "bar" beide zusammen benutzbar sind. – Veedrac

+0

@Veedrac: Das war nicht, was ich überhaupt in Frage gestellt hatte, aber ich sehe jetzt, wie es auch so gelesen werden kann. Mal sehen, was der OP von dieser Antwort halten wird. –