2016-05-18 23 views
1

Ich habe eine Liste von CSV-Dateien und ich möchte einen Iterator über die Zeilen aller Dateien erzeugen. Ich verwende so flat_map():Geliehene lokale Variable in verschachtelten Lambda

extern crate csv; 
extern crate rustc_serialize; 
use std::path::Path; 
use std::fs; 

// simple struct used by the csv crate to deserialize the csv line into this Value 
#[derive(RustcDecodable, RustcEncodable)] 
pub struct Value { 
    pub id: String, 
} 

// I have an iterator over some csv files, 
// I want an iterator of all the lines of all the files 
fn do_stuff<I>(files: I) 
    where I: Iterator<Item = std::path::PathBuf> 
{ 
    let iter = files.flat_map(|f| { 
     let mut rdr = csv::Reader::from_file(f).unwrap().has_headers(false); 

     rdr.decode() // <- decode() takes rdr by ref 
      .map(|r| { 
      let b: Value = r.unwrap(); 
      b.id //takes some values 
     }) 
    }); 
    // do stuff with iter 
} 

fn main() { 
    let paths: std::fs::ReadDir = fs::read_dir(".").unwrap(); 
    do_stuff(paths.map(|p| p.unwrap().path())); 
} 

jedoch der borrow checker damit nicht glücklich ist:

error: `rdr` does not live long enough 
rdr.decode().map(|r| { 
^~~ 
note: reference must be valid for the block suffix following statement 0 at 22:7... 
}); 
//do stuff with iter 
} 
note: ...but borrowed value is only valid for the block suffix following statement 0 at 16:76 
let mut rdr = csv::Reader::from_file(f).unwrap().has_headers(false); 

rdr.decode().map(|r| { 
    let b: Value = r.unwrap(); 
    b.id 
}) 

Die 2 verwendeten Lambda (die man in flat_map und die einen in map) nicht erfassen andere Variablen verstehe ich also nicht wirklich, warum der lokale rdr so lange leben muss.

Nun, die decode Funktion nimmt einen ref auf rdr, so scheint es, map einen Besitz ref rdr braucht ...

Antwort

3

Diese etwas pingelig ist, macht aber Sinn mit Rust Regeln. Der an flat_map übergebene Abschluss ist eine Funktion, die einen Iterator zurückgibt, der dann innerhalb des flat_map Iterators entleert wird. Was passiert, ist, dass der Iterator decode auf einen Bezug auf rdr lebt, aber rdr wird am Ende des Verschlusses fallen gelassen!

|f| { 
     let mut rdr = csv::Reader::from_file(f).unwrap().has_headers(false); 

     rdr.decode() // <- decode() takes rdr by ref 
      .map(|r| { 
      let b: Value = r.unwrap(); 
      b.id //takes some values 
     } // <--- Returns this iterator, which requires &'a mut rdr 
} // <--- rdr dropped here 
// <--- Uh oh, now we can't use the decoder, since rdr doesn't exist 

Die einfachste Abhilfe ist:

let v: Vec<_> = rdr.decode().map(...).collect(); 
v 

Dies liefert einen Vektor, der von flat_map iteriert wird. Es ist wahrscheinlich nicht die performanteste Lösung, aber es ist einfach.

wäre eine andere Lösung sein, eigene struct zu schreiben, die csv::Reader von Wert annimmt, und implementiert Iterator wie so:

fn next(&mut self) -> Option<WhateverType> { 
    self.rdr.decode().next().and_then(|v| { 
     v.unwrap().id 
    }) 
} 

Dann wie etwas zu tun, würden Sie wollen:

|f| { 
    let rdr = csv::Reader::from_file(f).unwrap().has_headers(false); 

    MyIterator::new(rdr) 
}