我有一个 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 个 lambda(flat_map
中的一个和 map
中的一个)没有捕获其他变量,因此我不太明白为什么本地 rdr
需要活那么久。
嗯,decode
函数在 rdr
上有一个 ref,因此似乎 map
需要一个拥有 rdr
的 ref >...
最佳答案
这有点挑剔,但符合 Rust 的规则。传递给 flat_map
的闭包是一个返回 迭代器的函数,该迭代器随后在 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)
}
关于rust - 在嵌套的 lambda 中借用局部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37300411/