借用嵌套lambda中的局部变量

时间:2016-05-18 13:00:46

标签: rust lifetime borrow-checker

我有一个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 ...

1 个答案:

答案 0 :(得分:3)

这有点挑剔,但对Rust的规则有意义。传递给public class CustomObject implements ICustomObject { protected String name; @AssistedInject public CustomObject(@Assisted String name){ this.name = name; } } 的闭包是一个返回迭代器的函数,然后在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
    })
}