Ci sono due soluzioni al problema. Cominciamo con la più semplice:
Aggiungi una vita per il vostro tratto
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
Ciò richiede di annotare il corso della vita in tutto il mondo si utilizza il tratto. Quando si implementa il tratto, è necessario fare un'implementazione generica:
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()
}
}
quando si richiede il tratto per un argomento generico, è anche necessario fare in modo che tutti i riferimenti al vostro oggetto trait hanno la stessa durata:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
implementare il tratto di riferimento per il tipo
Invece di implementare il tratto per il tipo, implementare per un riferimento al tipo. Il tratto non ha mai bisogno di sapere nulla sulla vita in questo modo.
La funzione di tratto deve quindi assumere il suo argomento in base al valore. Nel tuo caso si implementare il tratto per un riferimento:
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()
}
}
La funzione fooget
ora diventa semplicemente
fn fooget<T: Foo>(foo: T) {}
Il problema di questo è che la funzione fooget
non sa T
è in realtà un &Bar
. Quando chiami la funzione get
, stai effettivamente uscendo dalla variabile foo
. Non ti muovi fuori dall'oggetto, semplicemente muovi il riferimento. Se la tua funzione fooget
prova a chiamare due volte lo get
, la funzione non verrà compilata.
Se si desidera la vostra funzione fooget
per accettare solo gli argomenti in cui il tratto Foo
è implementata per i riferimenti, è necessario dichiarare esplicitamente questo limite:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
La clausola where
fa in modo che si chiama questa funzione solo per riferimenti in cui è stato implementato Foo
per il riferimento anziché il tipo. Potrebbe anche essere implementato per entrambi.
Tecnicamente, il compilatore potrebbe inferire automaticamente la durata in fooget_twice
così si potrebbe scrivere come
n fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
ma non è abbastanza yet intelligente.
Per i casi più complessi, è possibile utilizzare una funzione della ruggine che non è ancora implementata: Generic Associated Types (GATS). Il lavoro per questo viene monitorato in issue 44265.
Ho scelto di utilizzare la prima soluzione, poiché sembra imporre meno oneri dal punto di vista delle funzioni simili a "fooget". Il tratto è anche più esplicito con la seconda soluzione. – mbrt