在嵌套的 lambda 中借用局部变量

Borrowed local variable in nested lambda

我有一个 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 上进行引用,因此 map 似乎需要对 rdr...

的拥有引用

这有点挑剔,但符合 Rust 的规则。传递给 flat_map 的闭包是 returns 一个迭代器的函数,然后在 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

这个 returns 一个向量,由 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)
}