2016-07-15 7 views
3

Der folgende Code nicht kompiliert:Festlegen Lebensdauer, wenn das Werk Muster in Rust mit

trait Phone { 
    fn call(&self); 
} 

struct IPhone<'a> { 
    my_str: &'a str 
} 

impl<'a> Phone for IPhone<'a> { 
    fn call(&self) { 
     print!("{}", self.my_str); 
    } 
} 

trait Factory<'a, P: Phone> { 
    fn new_phone(&self, ms: &'a str) -> P; 
} 

struct IPhoneFactory; 
impl<'a> Factory<'a, IPhone<'a>> for IPhoneFactory { 
    fn new_phone(&self, ms: &'a str) -> IPhone<'a> { 
     return IPhone { 
      my_str: ms 
     }; 
    } 
} 

fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { 
    for _ in 0..10 { 
     let s = String::new(); 
     let p = f.new_phone(s.as_str()); 
     p.call(); 
    } 
} 

fn main() { 
    call_phone(IPhoneFactory); 
} 

ich folgende Fehlermeldung erhalten:

error: `s` does not live long enough 
     let p = f.new_phone(s.as_str()); 
          ^
note: reference must be valid for the lifetime 'a as defined on the block at 28:53... 
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { 
                ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 30:30 
     let s = String::new(); 
          ^

Ich will eine Fabrik haben können, dass gibt eine abstrakte Klasse zurück, aber wenn diese Klasse eine Referenz nimmt, kann ich nicht herausfinden, wie man die Lebensdauer richtig angibt.

Antwort

4

Sie haben Recht vor ut dass:

There is no reason for the reference to live as long as the factory, it only needs to live as long as the object the factory is creating (the factory itself doesn't store a reference to the string).

Aber die gebunden auf call_phone sagt etwas anderes

fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { ... } 

Dieser Code sagt, dass es ein einziges Leben für die ganze Fabrik, die für jedes Telefon verwendet wird. Sie wollen etwas anderes, möchte man sagen, dass f eine gute Fabrik für ist jede Lebensdauer:

fn call_phone<..., F: for<'a> Factory<'a, ...>>(f: F) { ... } 

Das andere Problem ist, dass in der Factory Charakterzug Definition:

trait Factory<'a, P: Phone> { 
    fn new_phone(&self, ms: &'a str) -> P; 
} 

Es gibt nichts Binde Lebensdauer ist von P bis ms. Die Eigenschaftsdefinition ermöglicht es dem zurückgegebenen Telefon, den String zu überleben, was für die Implementierung IPhone definitiv verboten sein sollte! Also, um es zu beheben, fügen wir ein Leben lang Parameter an das Phone Merkmal:

trait Phone<'a> { 
    fn call(&self); 
} 

Aber es gibt noch ein Problem. Wir können nicht wirklich, dass die Unterzeichnung schreiben:

fn call_phone<P: ???, F: for<'a> Factory<'a, P<'a>>(f: F) { ... } 

Da wir P sein, nicht eine Art wollen, sondern eine Familie von Typen (genauer gesagt, ein lifetime → type Konstruktor). Denken Sie daran, dass das Telefon in jeder Iteration der Schleife einen anderen Typ hat (da die Lebensdauer ein Teil eines Typs ist und die Lebensdauern in verschiedenen Iterationen von Schleifen unterschiedlich sind).

Fähigkeit, eine solche Unterschrift auszudrücken ist für die Zukunft Rust geplant, aber jetzt haben wir eine Abhilfe machen und das Telefon zugeordneten Art von Factory Eigenschaft zu machen:

trait Phone<'a> { 
    fn call(&self); 
} 

struct IPhone<'a> { 
    my_str: &'a str 
} 

impl<'a> Phone<'a> for IPhone<'a> { 
    fn call(&self) { 
     println!("{}", self.my_str); 
    } 
} 

trait Factory<'a> { 
    type Output: Phone<'a>; 
    fn new_phone(&self, ms: &'a str) -> Self::Output; 
} 

struct IPhoneFactory; 
impl<'a> Factory<'a> for IPhoneFactory { 
    type Output = IPhone<'a>; 
    fn new_phone(&self, ms: &'a str) -> IPhone<'a> { 
     IPhone { 
      my_str: ms 
     } 
    } 
} 

fn call_phone<F: for<'a> Factory<'a>>(f: F) { 
    for i in 0..10 { 
     let s = i.to_string(); 
     let p = f.new_phone(&s); 
     p.call(); 
    } 
} 

fn main() { 
    call_phone(IPhoneFactory); 
} 

Assoziierte Typ der Fabrik erlaubt produzieren Sie nur eine Art von Produkt, das ist vielleicht das, was Sie wollten.Wenn Sie verschiedene Implementierungen von Factory wollen verschiedene Output s haben, können Sie dies zu erreichen, indem Phantomtypen mit:

trait Phone<'a> { 
    type Phantom; 
    fn call(&self); 
} 

enum IPhonePhantom {} 

struct IPhone<'a> { 
    my_str: &'a str 
} 

impl<'a> Phone<'a> for IPhone<'a> { 
    type Phantom = IPhonePhantom; 
    fn call(&self) { 
     println!("{}", self.my_str); 
    } 
} 

trait Factory<'a, Selector> { 
    type Output: Phone<'a, Phantom=Selector>; 
    fn new_phone(&self, ms: &'a str) -> Self::Output; 
} 

struct MyFactory; 
impl<'a> Factory<'a, IPhonePhantom> for MyFactory { 
    type Output = IPhone<'a>; 
    fn new_phone(&self, ms: &'a str) -> IPhone<'a> { 
     IPhone { 
      my_str: ms 
     } 
    } 
} 

fn call_phone<Selector, F: for<'a> Factory<'a, Selector>>(f: F) { 
    for i in 0..10 { 
     let s = i.to_string(); 
     let p = f.new_phone(&s); 
     p.call(); 
    } 
} 

fn main() { 
    call_phone::<IPhonePhantom, _>(MyFactory); 
} 

Der Phantom zugehörige Typ auf das Phone Merkmale nicht unbedingt erforderlich sind, ist es nur notwendig, um den Telefontyp zu binden zu seinem Phantom-Typ und um sicherzustellen, dass Factory Implementierer nicht lügen.

+0

Das war sehr hilfreich und aufschlussreich, danke. –

0

Ihr Problem ist hier:

fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { 
// Factory has a lifetime 'a ----------^ 
// that is at least as long as the scope of call_phone 
    for _ in 0..10 { 
     let s = String::new(); // s is born here 
     let p = f.new_phone(s.as_str()); 
     // new reference ---^ 
     // new_phone definition requires this to have 
     // the same lifetime 'a as the Factory   
     p.call(); 
    } 
    // s is destroyed here, no references to s can 
    // still exist 
} // F is still alive 

Eine Sache, die Sie tun können, ist die &str als Parameter an call_phone vorbei, um sicherzustellen, dass die Referenz, solange die Funktion lebt:

fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F, s: &'a str) { 
    for _ in 0..10 { 
     let p = f.new_phone(s); 
     p.call(); 
    } 
} 

fn main() { 
    call_phone(IPhoneFactory, &"hello"); 
} 

Ein anderer arbeitet nicht mit Referenzen, aber lassen Sie Ihre struct IPhone besitzen die String

+0

Die Dinge, die Sie vorgeschlagen haben, beheben das Problem nicht wirklich für mich. Es gibt keinen Grund dafür, dass die Referenz so lange lebt wie die Fabrik, sie muss nur so lange leben, wie das Objekt, das die Fabrik erstellt (die Fabrik selbst speichert keinen Verweis auf die Zeichenkette). Was Sie vorgeschlagen haben, funktioniert nicht, weil ich wirklich neue Objekte erstellen und Referenzen auf Objekte geben möchte, die vom Factory-Erstellungsaufruf in der Factory erstellt wurden. (Das funktioniert völlig, wenn ich die Fabrik nicht hatte.) Hoffentlich macht das Sinn. –

+0

Anders ausgedrückt: Wie entkopple ich die Lebensdauer der Referenz, die ich an new_phone übergebe, von der Lebensdauer der Factory, und habe nur die Lebensdauer der Struktur, die die Factory erstellt. –

Verwandte Themen