如何使用HashMap作为累加器进行折叠?

时间:2015-08-07 18:22:56

标签: rust

此代码有效:

let stdin = std::io::stdin();
let mut rdr = csv::Reader::from_reader(stdin);
let mut hmap = HashMap::<String, u64>::new();

rdr.records()
    .map(|r| r.unwrap())
    .fold((), |_, item| {
        // TODO: Is there a way not to have to copy item[col] every time?
        let counter = hmap.entry(item[col].to_string()).or_insert(0);
        *counter += 1;
    });

此代码失败并显示以下消息:“无法移出acc,因为它是借用的”

let stdin = std::io::stdin();
let mut rdr = csv::Reader::from_reader(stdin);
let hmap = rdr.records()
    .map(|r| r.unwrap())
    .fold(HashMap::<String, u64>::new(), |mut acc, item| {
        // TODO: Is there a way not to have to copy item[col] every time?
        let counter = acc.entry(item[col].to_string()).or_insert(0);
        *counter += 1;
        acc
    });

1 个答案:

答案 0 :(得分:14)

你不能从闭包中返回acc,因为你有一个仍然存在的可变借位(counter)。

这是Rust编译器的限制(特别是借用检查器)。启用non-lexical lifetimes后,原始代码将起作用:

#![feature(nll)]

use std::collections::HashMap;

fn main() {
    let hmap = vec![1, 2, 3].iter().fold(HashMap::new(), |mut acc, _| {
        let counter = acc.entry("foo".to_string()).or_insert(0);
        *counter += 1;
        acc
    });

    println!("{:?}", hmap);
}

在NLL之前,编译器对借用持续多长时间过于保守。要解决这个问题,您可以引入一个新范围来约束可变借用:

use std::collections::HashMap;

fn main() {
    let hmap = vec![1, 2, 3].iter().fold(HashMap::new(), |mut acc, _| {
        {
            let counter = acc.entry("foo".to_string()).or_insert(0);
            *counter += 1;
        }
        acc
    });

    println!("{:?}", hmap);
}

您还可以阻止借款超出其所需的界限:

use std::collections::HashMap;

fn main() {
    let hmap = vec![1, 2, 3].iter().fold(HashMap::new(), |mut acc, _| {
        *acc.entry("foo".to_string()).or_insert(0) += 1;
        acc
    });

    println!("{:?}", hmap);
}
  

我认为Rust会在counter返回后知道acc会超出范围

这是可以理解的,并且涉及非词汇生命期讨论。 “好”的消息是Rust在被引用的东西移动时,引用如何工作是一致的。在这种情况下,您将累加器移动到“输出槽”。您也可以通过普通函数看到这一点:

fn foo(mut s: Vec<u8>) -> Vec<u8> {
    let borrow = &mut s[0];
    s
}

fn main() {}

但实际上,它与移动引用变量完全相同:

fn main() {
    let mut s = Vec::<u8>::new();
    let borrow = &mut s[0];
    let s2 = s;
}

这两个都在NLL之前失败并且之后继续工作。