2014-07-04 7 views
11

Ho un vita problema, sto cercando di implementare un iteratore tornare i suoi articoli per riferimento, ecco il codice:Iterator restituzione degli articoli per riferimento, questione di vita

struct Foo { 
    d: [u8; 42], 
    pos: usize 
} 

impl<'a> Iterator<&'a u8> for Foo { 
    fn next<'a>(&'a mut self) -> Option<&'a u8> { 
     let r = self.d.get(self.pos); 
     if r.is_some() { 
     self.pos += 1; 
     } 
     r 
    } 
} 

fn main() { 
    let mut x = Foo { 
     d: [1; 42], 
     pos: 0 
    }; 

    for i in x { 
     println!("{}", i); 
    } 
} 

Tuttavia questo codice doesn' t compilare correttamente, ottengo un problema relativo alla durata dei parametri, qui è l'errore corrispondente:

$ rustc test.rs 
test.rs:8:5: 14:6 error: method `next` has an incompatible type for trait: expected concrete lifetime, but found bound lifetime parameter 
test.rs:8  fn next<'a>(&'a mut self) -> Option<&'a u8> { 
test.rs:9   let r = self.d.get(self.pos); 
test.rs:10   if r.is_some() { 
test.rs:11    self.pos += 1; 
test.rs:12   } 
test.rs:13   r 
      ... 
test.rs:8:49: 14:6 note: expected concrete lifetime is the lifetime 'a as defined on the block at 8:48 
test.rs:8  fn next<'a>(&'a mut self) -> Option<&'a u8> { 
test.rs:9   let r = self.d.get(self.pos); 
test.rs:10   if r.is_some() { 
test.rs:11    self.pos += 1; 
test.rs:12   } 
test.rs:13   r 
      ... 
error: aborting due to previous error 

C'è qualcuno che ha un idea di come risolvere questo problema e ancora della restituzione degli articoli per riferimento?

Almeno cosa significa questo messaggio: durata del calcestruzzo prevista, ma trovato il parametro di durata limite?

risposta

14

Note on the version of Rust used: at the time this question and answer were written, the Iterator trait used generics; it has changed to use associated types and is now defined thus:

pub trait Iterator { 
    type Item; 

    fn next(&mut self) -> Option<Self::Item>; 
    … 
} 

And so the incorrect implementation shown here would be like this:

impl<'a> Iterator for Foo { 
    type Item = &'a u8; 

    fn next<'a>(&'a mut self) -> Option<&'a u8>; 
} 

In practical terms this affects nothing; it is merely that A becomes Self::Item .

La definizione del Iterator tratto è così:

pub trait Iterator<A> { 
    fn next(&mut self) -> Option<A>; 
    … 
} 

Nota con attenzione: fn next(&mut self) -> Option<A>.

Qui è quello che hai:

impl<'a> Iterator<&'a u8> for Foo { 
    fn next<'a>(&'a mut self) -> Option<&'a u8>; 
} 

Nota con attenzione: fn next<'a>(&'a mut self) -> Option<&'a u8>.

Ci sono diversi problemi qui:

  1. Hai introdotto un nuovo parametro generico <'a> che non dovrebbe essere lì. Per motivi di praticità e per sottolineare ciò che è successo qui, dovrò duplicare lo 'a definito sul blocco impl ρ₀ e lo 'a definito sul metodo ρ₁. Non sono la stessa cosa.

  2. La durata di vita di &mut self è diversa da quella del tratto.

  3. La durata del tipo di ritorno è diverso per il tratto: dove A è &'ρ₀ u8, il tipo di ritorno utilizza al posto del A&'ρ₁ u8. Si aspettava la vita concreta ρ₀ ma ha trovato invece la vita ρ₁. (Io non sono certo proprio ciò che il “bound” bit significa, quindi terrò tranquillo su di esso, diventato sbagliato.)

Ecco cosa ciò equivale a: non è possibile collegare la durata di l'oggetto che si sta iterando su &mut self. Invece, deve essere associato a qualcosa nel tipo per cui stai implementando il tratto. Per fare un esempio, l'iterazione su elementi in una sezione viene effettuata creando un nuovo oggetto iteratore collegato alla slice di base, impl<'a, T> Iterator<&'a T> for Items<'a, T>. Espressa in un altro modo, il modo in cui sono progettati i tratti di iterazione non è, se si producono riferimenti, di restituire qualcosa all'interno di self, ma piuttosto di restituire qualcosa all'interno di un altro oggetto a cui si ha un riferimento.

Per la vostra specifica, presumibilmente semplice esempio, si dovrebbe neanche smettere di riferimenti rendimento, o modificare in modo che l'oggetto iteratore non contiene i dati che si effettua l'iterazione-lasciarlo solo contiene un riferimento ad esso, ad esempio,&'a [T] o anche qualcosa come Items<'a, T>.

+0

Risposta molto utile. Devo dire che 1 sto lottando parecchio per capire e usare correttamente i tipi lifetime/generici/tratti specifici specificati in struct e traits e quelli usati nei metodi, da qui il mio cattivo uso di un a ovunque in questo esempio. 2- Penso di aver capito bene il tuo punto, ho provato a grep il codice Rust e le sue librerie per scoprire come è stato gestito un caso simile e quello che hai detto sembra corrispondere a diversi casi in cui un parametro lifetime viene utilizzato in una struttura con un attributo referenziato come ad esempio l'iteratore di Splits in libcore/slice.rs. Grazie per il tuo aiuto – user3762625

+0

A volte potresti trovare gli esempi nella libreria standard di difficile comprensione; per esempio, 'Items' è in realtà qualcosa prodotto con una macro, quindi è necessario capire le basi delle macro di Rust prima che tu possa essere in grado di farlo! Siate pronti in ogni momento a passare da irc: //irc.mozilla.org/#rust, ci sono sempre persone che aiutano. –

+0

Notare che esiste un motivo importante per la restituzione di un elemento la cui durata è * non * collegata all'iteratore: ciò consente l'implementazione di * trasformazioni * nell'iterazione, che è un'operazione molto difficile da eseguire in C++, ad esempio. –