2016-08-02 2 views
9

Ich kann nicht verstehen, warum eine veränderbare geliehene Variable noch geliehen wird, nachdem der Umfang des Kreditnehmers endet. Es sieht aus wie es sich bezieht Nutzung trait, aber ich sehe nicht, warum:Kann Variable nicht ausleihen, wenn der Kreditorenbereich endet

fn main() { 
    let mut a = 10; 
    test::<FooS>(&mut a); 
    println!("out {:?}", a) 
} 

trait Foo<'a> { 
    fn new(data: &'a mut u32) -> Self; 
    fn apply(&mut self); 
} 

struct FooS<'a> { 
    data: &'a mut u32, 
} 

impl<'a> Foo<'a> for FooS<'a> { 
    fn new(data: &'a mut u32) -> Self { 
     FooS { data: data } 
    } 

    fn apply(&mut self) { 
     *self.data += 10; 
    } 
} 

fn test<'a, F>(data: &'a mut u32) 
    where F: Foo<'a> 
{ 
    { 
     // let mut foo = FooS {data: data}; // This works fine 
     let mut foo: F = Foo::new(data); 
     foo.apply(); 
    } // foo scope ends here 
    println!("{:?}", data); // error 
} // but borrowed till here 

try online

error: cannot borrow `data` as immutable because `*data` is also borrowed as mutable [--explain E0502] 
    --> <anon>:34:22 
31 |>   let mut foo: F = Foo::new(data); 
    |>         ---- mutable borrow occurs here 
... 
34 |>  println!("{:?}", data); // error 
    |>      ^^^^ immutable borrow occurs here 
35 |> } // but borrowed till here 
    |> - mutable borrow ends here 
+1

https://play.rust-lang.org/?gist=dabe17d66a14e72c2ca67e064ca26601&version=nightly&backtrace=0 funktioniert. so ... – Jacob

+0

Trotzdem scheint dies ein Fehler zu sein. – JDemler

Antwort

8

Die test Funktion erfordert diese Art FFoo<'a> implementiert. Die 'a gibt es einen Lebensdauerparameter, der an die Funktion übergeben wird. Lebensdauerparameter stellen immer Lebensdauern dar, die länger leben als der Funktionsaufruf - weil es keinen Weg gibt, wie ein Anrufer eine Referenz mit einer kürzeren Lebenszeit liefern könnte; Wie können Sie einen Verweis auf eine lokale Variable von eine andere Funktion übergeben? - und für die Zwecke der Borrow-Prüfung (die lokal für eine Funktion ist), betrachtet der Compiler, dass das Borrow den gesamten Funktionsaufruf abdeckt.

Deshalb, wenn Sie eine Instanz von F aus dem Aufruf von Foo::new erstellen, erstellen Sie ein Objekt, das 'a etwas mit Lebenszeit leiht, ein Leben lang, die den gesamten Funktionsaufruf abdeckt.

Es ist wichtig zu verstehen, dass, wenn Sie test::<FooS> nennen, der Compiler füllt tatsächlich im Leben Parameter für FooS<'a>, so dass Sie am Ende test::<FooS<'a>> Aufruf, wo 'a die Region ist, der die Anweisung umfasst, die den Funktionsaufruf enthält (weil &mut a ist ein temporärer Ausdruck). Daher denkt der Compiler, dass der FooS, der in test konstruiert würde, etwas bis zum Ende der Anweisung mit dem Aufruf an test ausleihen würde!

Lassen Sie sich Kontrast dies mit der nicht-generischen Version:

let mut foo = FooS {data: data}; 

In dieser Version wählt der Compiler eine konkrete Lebenszeit für FooS<'a> in test, anstatt in main, so wird es den Block Suffix vom Ende erstreckt wählen der let Anweisung an das Ende des Blocks, was bedeutet, dass der nächste Kredit von data nicht überlappt und es gibt keinen Konflikt.

Was Sie wirklich wollen, ist, dass FFoo<'x> für einige Lebensdauer implementieren 'x, die kürzer als 'a ist, und was am wichtigsten ist, muss die Lebensdauer einer Region innerhalb der Funktion, nicht ein umschließt ein wie 'a ist.

Die aktuelle Lösung von Rust für dieses Problem sind höherrangige Merkmalsgrenzen. Es sieht wie folgt aus:

fn test<'a, F>(data: &'a mut u32) 
    where F: for<'x> Foo<'x> 
{ 
    { 
     let mut foo: F = Foo::new(data); 
     foo.apply(); 
    } 
    println!("{:?}", data); 
} 

In Worten, es bedeutet den Typ F muss Foo<'x> für jede mögliche 'x implementieren.

Während diese Version von test auf eigene kompiliert, können wir nicht wirklich einen Typ liefern, die diese Bedingung erfüllt, da für jede mögliche Lebensdauer 'a gibt es einen bestimmten Typ FooS<'a>, die nur Foo<'a> implementiert.Wenn FooS kein Lebensdauer Parameter hatte und die impl von Foo für FooS sah wie folgt aus:

impl<'a> Foo<'a> for FooS { 

dann wäre es in Ordnung sein, da es eine einzige Art ist, die FooS'aFoo<'a> für jede mögliche Lebensdauer implementiert.

Natürlich können Sie den Lebensdauerparameter FooS nicht entfernen, da er einen ausgeliehenen Zeiger enthält. Die richtige Lösung für dieses Problem ist eine Funktion, die Rust noch nicht hat: die Fähigkeit, einen Typkonstruktor (anstelle eines vollständig konstruierten Typs) als generischen Parameter an eine Funktion zu übergeben. Mit dieser Funktion könnten wir test mit FooS aufrufen, einen Typenkonstruktor, der einen Lebensdauerparameter benötigt, um einen konkreten Typ zu erzeugen, ohne die konkrete Lebensdauer am Aufrufort anzugeben, und der Aufrufer könnte seine eigene Lebensdauer bereitstellen.

+0

Danke für ausführliche Antwort. Noch ein unklarer Moment für mich. Wie Sie bereits sagten _ "Lebenszeitparameter stellen immer Lebensdauern dar, die länger leben als der Funktionsaufruf, und für die Zwecke der Kreditprüfung (die lokal für eine Funktion ist) betrachtet der Compiler den Kredit als den gesamten Funktionsaufruf." _. Warum Compiler-Compiler dies berücksichtigen? Wir haben ein Leben, das länger lebt als der Funktionsumfang. Aber wie bezog es sich auf diesen Kredit, der den gesamten Funktionsaufruf abdeckt? Ist es Details der Implementierung oder Teil der Spezifikation? –

+0

Es gibt noch keine richtige "Spezifikation" von Rust, so dass die Implementierung einer Spezifikation am nächsten kommt. Für Ihre andere Frage, siehe meine Bearbeitung. –

Verwandte Themen