2015-11-16 12 views
15

Ich habe diese Eigenschaft und einfache Struktur:Wie lege ich Lebensdauerparameter in einem zugeordneten Typ fest?

use std::path::{Path, PathBuf}; 

trait Foo { 
    type Item: AsRef<Path>; 
    type Iter: Iterator<Item = Self::Item>; 

    fn get(&self) -> Self::Iter; 
} 

struct Bar { 
    v: Vec<PathBuf>, 
} 

Ich möchte die Foo Merkmal für Bar umzusetzen:

impl Foo for Bar { 
    type Item = PathBuf; 
    type Iter = std::slice::Iter<PathBuf>; 

    fn get(&self) -> Self::Iter { 
     self.v.iter() 
    } 
} 

Allerdings erhalte ich diese Fehlermeldung:

error[E0106]: missing lifetime specifier 
    --> src/main.rs:16:17 
    | 
16 |  type Iter = std::slice::Iter<PathBuf>; 
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter 

Ich habe keine Möglichkeit gefunden, Lebensdauern innerhalb dieses zugeordneten Typs anzugeben. Insbesondere möchte ich zum Ausdruck bringen, dass der Iterator die Lebensdauer self nicht überleben kann.

Wie muss ich die Foo-Eigenschaft oder die Bar-Trait-Implementierung ändern, damit dies funktioniert?

Rust playground

Antwort

13

Es gibt zwei Lösungen für Ihr Problem. Beginnen wir mit dem einfachsten anfangen:

ein Leben lang zu Ihrer Eigenschaft hinzufügen

trait Foo<'a> { 
    type Item: AsRef<Path>; 
    type Iter: Iterator<Item = Self::Item>; 

    fn get(&'a self) -> Self::Iter; 
} 

Dies erfordert, dass Sie die Lebensdauer zu annotieren überall Sie das Merkmal verwenden. Wenn Sie das Merkmal implementieren, müssen Sie eine generische Implementierung tun:

impl<'a> Foo<'a> for Bar { 
    type Item = &'a PathBuf; 
    type Iter = std::slice::Iter<'a, PathBuf>; 

    fn get(&'a self) -> Self::Iter { 
     self.v.iter() 
    } 
} 

Wenn Sie das Merkmal für ein generisches Argument benötigen, müssen Sie auch sicherstellen, dass alle Verweise auf Ihre Eigenschaft Objekt die gleiche Lebensdauer haben:

fn fooget<'a, T: Foo<'a>>(foo: &'a T) {} 

implementieren sie die Eigenschaft für einen Verweis auf Ihre Art

Anstatt das Merkmal für Ihre Art der Umsetzung implementieren es für einen Verweis auf Ihre Art. Das Merkmal muss auf diese Weise nie etwas über die Lebenszeit wissen.

Die Merkmalsfunktion muss dann ihr Argument nach Wert übernehmen. In Ihrem Fall werden Sie das Merkmal für eine Referenz implementieren:

trait Foo { 
    type Item: AsRef<Path>; 
    type Iter: Iterator<Item = Self::Item>; 

    fn get(self) -> Self::Iter; 
} 

impl<'a> Foo for &'a Bar { 
    type Item = &'a PathBuf; 
    type Iter = std::slice::Iter<'a, PathBuf>; 

    fn get(self) -> Self::Iter { 
     self.v.iter() 
    } 
} 

Ihre fooget Funktion jetzt wird einfach

fn fooget<T: Foo>(foo: T) {} 

Das Problem dabei ist, dass die fooget Funktion nicht T in Wirklichkeit ein weiß ist &Bar. Wenn Sie die get Funktion aufrufen, bewegen Sie sich tatsächlich aus der Variablen foo heraus. Sie bewegen sich nicht aus dem Objekt heraus, Sie verschieben nur die Referenz. Wenn Ihre fooget Funktion versucht, get zweimal aufzurufen, wird die Funktion nicht kompiliert.

Wenn Sie Ihre fooget Funktion wollen nur Argumente akzeptieren, wenn das Foo Merkmal für Referenzen implementiert ist, müssen Sie ausdrücklich darauf hinweisen diese Schranke:

fn fooget_twice<'a, T>(foo: &'a T) 
where 
    &'a T: Foo, 
{} 

Die where Klausel stellt sicher, dass Sie nur diese Funktion aufrufen für Referenzen, wo Foo für die Referenz anstelle des Typs implementiert wurde. Es kann auch für beide implementiert werden.

Technisch könnte der Compiler automatisch die Lebensdauer in fooget_twice schließen, so schreiben Sie könnten es als

n fooget_twice<T>(foo: &T) 
where 
    &T: Foo, 
{} 

aber es ist nicht klug genug yet.


Bei komplizierteren Fällen können Sie eine Rust-Funktion verwenden, die noch nicht implementiert ist: Generic Associated Types (GATS). Arbeiten dafür werden in issue 44265 verfolgt.

+0

Ich habe gewählt, um die erste Lösung zu verwenden, da es scheinbar weniger Belastung aus der Sicht der "fooget" wie Funktionen auferlegt. Das Merkmal ist auch expliziter als die zweite Lösung. – mbrt

Verwandte Themen