2016-05-18 6 views
1

У меня есть список файлов CSV, и я хочу создать итератор по строкам всех файлов. Я, таким образом, с помощью flat_map():Заимствованная локальная переменная в вложенной лямбда

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())); 
} 

Однако заимствуют проверки не доволен этим:

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 
}) 

2-используется лямбда (один в flat_map и один в map) не улавливают другие переменные, я, таким образом, не совсем понимаю, почему местный rdr должен так долго жить.

Ну функция decode принимает реф на rdr, таким образом, кажется, map нуждается в владеющую реф к rdr ...

ответ

3

Это немного привередливо, но имеет смысл с правилами Руста. Закрытие, переданное в flat_map, является функцией, которую возвращает итератор, который затем сливается в итераторе . Что происходит, так это то, что итератор decode опирается на ссылку rdr, но rdr отбрасывается в конце закрытия!

|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 

Самый простой обходной путь является:

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

Это возвращает вектор, который итерированным сверх flat_map. Это, вероятно, не самое эффективное решение, но это просто.

Другим решением было бы написать свой собственный struct, который принимает csv::Reader по значению, и реализует Iterator так:

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

Тогда вы хотите сделать что-то вроде:

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

    MyIterator::new(rdr) 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^