2016-07-26 16 views
5

Ich habe Sammlungen auf der Festplatte abgelegt. Wenn angefordert, sollten diese Sammlungen abgerufen werden (kein Problem), und es sollte eine iterator erstellt werden, die Verweise auf die abgerufenen Werte zurückgibt. Nachdem die iterator gelöscht wurde, brauche ich die Sammlung nicht mehr. Ich möchte, dass es auch fallen gelassen wird.Iterate über Sammlung. Lassen Sie es fallen, sobald der Iterator gelöscht wird

Was ich bisher versucht:

  1. Die Iterator besitzt die Sammlung. Das hat für mich am meisten Sinn gemacht, aber es ist nicht möglich; Ich bin mir nicht ganz sicher warum. Einige sagen, dass die Iterator Traits 'Methodensignatur für next das Problem ist. (example)

  2. Referenzzählung: Die Retriever gibt eine Rc<Vec<usize>> zurück. Ich stieß auf die gleichen Probleme wie im besitzenden Iterator. (example)

  3. Den Retriever die Sammlung besitzen lassen und eine Referenz darauf verteilen. Ich habe versucht, den Retriever mit Interior-Mutabilität zu implementieren (RefCell<HashMap>), aber ich kann keine Referenzen in die HashMap mit ausreichend langen Lebenszeiten zurückgeben.

Ich sehe zwei grundlegende Möglichkeiten mit diesem.

  1. Der Besitz Retriever Transfers. Dann müsste die Iterator die Daten besitzen. Etwas in den Zeilen:

    use std::slice::Iter; 
    
    fn retrieve(id: usize) -> Vec<usize> { 
        //Create Data out of the blue (or disk, or memory, or network. I dont care) 
        //Move the data out. Transfer ownership 
        let data = vec![0, 1, 2, 3]; 
        data 
    } 
    
    fn consume_iterator<'a, TIterator: Iterator<Item=&'a usize>>(iterator: TIterator) { 
        for i in iterator { 
         println!("{}", i); 
        } 
    } 
    
    fn handler<'a>(id: usize) -> Iter<'a, usize> { 
        //handle_request now owns the vector. 
        //I now want to build an owning iterator.. 
        //This does of course not compile as vector will be dropped at the end of this method 
        retrieve(id).iter() 
    } 
    
    fn main() { 
        consume_iterator(handler(0)) 
    } 
    
  2. Der Retriever besitzt die Sammlung. Aber dann treten zwei neue Probleme auf:

    1. Wie kann ich die Daten löschen, wenn der Iterator außer Reichweite ist?
    2. Wie sage ich dem Borrow-Checker, dass ich die Sammlung lange genug besitzen werde?

    use std::cell::{Ref, RefCell}; 
    
    struct Retriever { 
        //Own the data. But I want it to be dropped as soon as the references to it go out of scope. 
        data: RefCell<Vec<usize>> 
    } 
    
    impl Retriever{ 
    
        fn retrieve<'a>(&'a self, id: usize) -> Ref<'a, Vec<usize>> { 
         //Create Data out of the blue (or disk, or memory, or network. I dont care) 
         //Now data can be stored internally and a referece to it can be supplied. 
         let mut data = self.data.borrow_mut(); 
         *data = vec![0, 1, 2, 3]; 
         self.data.borrow() 
        } 
    
    } 
    
    fn consume_iterator<'a, TIterator: Iterator<Item=&'a usize>>(iterator: TIterator) { 
        for i in iterator { 
         println!("{}", i); 
        } 
    } 
    
    
    fn handler<'a>(ret: &'a Retriever, id: usize) -> IterWrapper<'a> { 
        //andle_request now has a reference to the collection 
        //So just call iter()? Nope. Lifetime issues. 
        ret.retrieve(id).iter()   
    } 
    
    fn main() { 
        let retriever = Retriever{data: RefCell::new(Vec::new())}; 
        consume_iterator(handler(&retriever, 0)) 
    } 
    

Ich fühle mich hier verloren Bit und offensichtlich etwas bin mit Blick auf.

+1

Ich denke, Sie brauchen ['std :: vec :: IntoIter'] (https://doc.rust-lang.org/stable/std/vec/struct.IntoIter.html): https: // play. rust-lang.org/?gist=0e6c171c08fc7a314c44ac98f3dd398e&version=stable&backtrace=0 Aber dann könnte man auch direkt 'IntoIter' verwenden. – Dogbert

+0

Während dies kompiliert wird, verschiebt 'IntoIter' die Werte. Das empfangende Ende des 'Iterators' erwartet Verweise auf die Werte. Ich werde meine Frage entsprechend aktualisieren – JDemler

+3

Ich fürchte, Sie haben irgendwie in das [XY Problem] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) gerannt ... Warum Benötigen Sie Verweise auf die Werte, wenn Sie die Werte danach nicht benötigen? Der ganze Punkt der Kreditaufnahme ist so, dass Sie den Besitz behalten, aber anscheinend wollen Sie nicht den Besitz behalten. –

Antwort

1

Ich war jetzt endlich in der Lage eine relativ statisfying Lösung zu implementieren:

Ausblenden der Veränderlichkeit von Iteratoren innen Cell s:

pub trait OwningIterator<'a> { 
    type Item; 
    fn next(&'a self) -> Option<Self::Item>; 
} 

Eine Struktur nun eine Position Cell d braucht Iteration ohne Mutation zu ermöglichen. Als Beispiel ist hier die Implementierung einer Struktur, die sowohl besitzt und über ein Arc<Vec<T>> laufen kann:

pub struct ArcIter<T> { 
    data: Arc<Vec<T>>, 
    pos: Cell<usize>, 
} 

impl<'a, T: 'a> OwningIterator<'a> for ArcIter<T> { 
    type Item = &'a T; 

    fn next(&'a self) -> Option<Self::Item> { 
     if self.pos.get() < self.data.len() { 
      self.pos.set(self.pos.get() + 1); 
      return Some(&self.data[self.pos.get() - 1]); 
     } 
     None 
    } 
} 

Als ich in der Lage war, diese Art von Iteratoren hinter Schnittstellen zu verstecken und lassen die Benutzer nur „real“ Griff Iteratoren Ich denke, das ist eine akzeptable Abweichung vom Standard.

Danke an alle, die mit Ideen beigetragen haben, die mir letztendlich geholfen haben, diese Lösung zu finden.

4

Der Iterator besitzt die Sammlung. [Oder Miteigentum über Referenzzählung]

ContainerIterator { 
    data: data, 
    iter: data.iter(), 
} 

Nein, Sie cannot have a value and a reference to that value in the same struct.

Lassen Sie den Retriever die Sammlung besitzen und geben Sie einen Verweis darauf.

Nein, Sie cannot return references to items owned by the iterator.

Wie commen gesagt haben, verwenden IntoIter zum Iterator Besitz der Gegenstände zu übertragen und sie dann als die iterierten Werte auszuteilen:

use std::vec::IntoIter; 

struct ContainerIterator { 
    iter: IntoIter<usize>, 
} 

impl Iterator for ContainerIterator { 
    type Item = usize; 

    fn next(&mut self) -> Option<Self::Item> { 
     self.iter.next() 
    } 
} 

fn main() { 
    let data = vec![0, 1, 2, 3]; 
    let cont = ContainerIterator { iter: data.into_iter() }; 

    for x in cont { 
     println!("Hi {}", x) 
    } 
} 

Wenn Sie Muss Rückkehr Referenzen ... dann brauchen Sie um das Ding, das sie besitzt, für die ganze Zeit zu behalten, dass alle Referenzen vorhanden sind.

Wie kann ich die Daten löschen, wenn der Iterator außer Reichweite ist?

Durch den Verzicht auf den Wert mit mehr:

fn main() { 
    { 
     let loaded_from_disk = vec![0, 1, 2, 3]; 
     for i in &loaded_from_disk { 
      println!("{}", i) 
     } 
     // loaded_from_disk goes out of scope and is dropped. Nothing to *do*, per se. 
    } 
} 

Wie sage ich dem Borge-checker, dass ich lange genug, um die Sammlung besitzen werde?

Durch den Besitz der Sammlung lang genug. Es gibt keinen geheimen Händedruck, den die Rust Illuminati mit dem Border Checker benutzen. Der Code muss nur so strukturiert sein, dass die ausgeliehene Sache nicht ungültig wird, solange der Kredit aussteht. Sie können es nicht verschieben (die Speicheradresse ändern) oder es löschen (die Speicheradresse ändern).