2014-12-03 35 views
8

Lo schema di un tratto sicuro da oggetti Foo e un tratto di estensione (potenzialmente pericoloso) FooExt implementato per tutte le istanze di Foo sembra diventare standard ora.Limitazioni dei tratti di estensione

https://github.com/rust-lang/rfcs/pull/445

Questo è un problema per me, nel caso di Iterator<A>, come ho una libreria che sostituisce il metodo predefinito IteratorExt#last() del vecchio iteratore tratto (la biblioteca di fondo ha un'implementazione efficiente di last()). Questo è ormai impossibile, perché per qualsiasi A, ci sarà sempre un'implementazione di tratto in conflitto di IteratorExt, quella che libcore fornisce già per tutti Iterator<A>.

iterator.rs:301:1: 306:2 error: conflicting implementations for trait `core::iter::IteratorExt` [E0119]

iterator.rs:301 impl<'a, K: Key> iter::IteratorExt<Vec<u8>> for ValueIterator<'a,K,Vec<u8>> { 
iterator.rs:302 fn last(&mut self) -> Option<Vec<u8>> { 
iterator.rs:303  self.seek_last(); 
iterator.rs:304  Some(self.value()) 
iterator.rs:305 } 
iterator.rs:306 } 
... 

Ora, per quanto vedo, ho due opzioni:

  • avere il mio carattere e la mia last() implementazione. Ciò significherebbe conflitti se viene importato IteratorExt a meno che non venga usato con attenzione. Questo ha anche il pericolo di utilizzare accidentalmente una versione inefficiente di last() se viene utilizzata la versione da IteratorExt. Avrei libero accesso a IteratorExt.
  • hanno il mio tratto e denominano il metodo in modo diverso (seek_last()). Svantaggio: chiedo all'utente di imparare il vocabolario e di favorire sempre il mio metodo rispetto a quello fornito da IteratorExt. Lo stesso problema: vorrei evitare l'uso accidentale di last().

C'è qualche altra soluzione migliore che mi manca?

risposta

2

A partire da rustc 0.13.0-nightly (8bca470c5 2014-12-08 00:12:30 +0000) che definisce last() come un metodo inerente sul tipo dovrebbe funzionare.

#[deriving(Copy)] 
struct Foo<T> {t: T} 

impl<T> Iterator<T> for Foo<T> { 
    fn next(&mut self) -> Option<T> { None } 
} 

// this does not work 
// error: conflicting implementations for trait `core::iter::IteratorExt` [E0119] 
// impl<T> IteratorExt<T> for Foo<T> { 
// fn last(mut self) -> Option<T> { None } 
//} 

// but this currently does 
impl<T> Foo<T> { 
    fn last(mut self) -> Option<T> { Some(self.t) } 
} 


fn main() { 
    let mut t = Foo{ t: 3u }; 
    println!("{}", t.next()) 
    println!("{}", t.last()) // last has been "shadowed" by our impl 
    println!("{}", t.nth(3)) // other IteratorExt methods are still available 
} 

Dal momento che non dovresti usare tratti Extension come limiti generici (ma solo per fornire metodi aggiuntivi), questo dovrebbe funzionare in teoria per lo scenario, come si può avere il proprio tipo e la sua impl nello stesso cassa.

Gli utenti del tuo tipo utilizzeranno il metodo last inerente anziché quello su IteratorExt ma potranno comunque utilizzare gli altri metodi su IteratorExt.

+0

Non riesco a farlo funzionare con 'rustc 0.13.0-nightly (193390d0e 2014-12-11 22:56:54 +0000)'. Non sovrascrive il metodo, avverte per 'dead_code'. Mi piace come soluzione, però. – Skade

+0

Scratch che, non ho reso pubblico il metodo per usarlo in un'altra cassa. Funziona con 'pub fn last (...)'. – Skade

+0

Generalmente funziona in codice generico che consuma un tipo di questo tipo (con Iterator associato) e probabilmente vive in un'altra cassa? – sellibitze

0

last deve essere spostato su Iterator, anziché su IteratorExt.

IteratorExt è necessario quando si utilizza Box<Iterator> oggetti, per consentire di chiamare metodi generici su di loro (ad esempio map), perché non si può mettere un metodo generico in una vtable. Tuttavia, last non è generico, quindi può essere inserito in Iterator.

+0

Questa potrebbe essere una soluzione a questo problema specifico, ma la domanda è chiedere una soluzione generale per i tratti di estensione. – Shepmaster

Problemi correlati