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

标签 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 上有一个 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/

相关文章:

rust - 我可以将可变切片引用重新分配给自身的子切片吗?

c++ - 为什么这段代码涉及使用对临时段错误的引用,尽管它似乎正确地管理了生命周期?

rust - Rust 编译器什么时候不能证明借用是不相交的?

rust - 缓存自引用函数导致 Rust

arrays - 将字符串数组传递给函数而无需指定 N(编译时间常数)

rust - 如何返回包含 serde_json::Value 的结果?

rust - 可以选择在自定义迭代器 `skip` 函数中调用 `next()`

rust - 缓存/内存与对象生命周期

rust - 指定特征边界时如何指定临时生命周期?

generics - 可变借位在循环的上一迭代中从此处开始