从地图中获取价值并在相同范围内修改地图

时间:2019-03-01 20:38:33

标签: rust

这是一个简单的程序,它在map中查找先前的值并将新值放入其中。它不会编译,因为第一个查询不可变地借用了map,而insert想要不可变地借用了map

use std::collections::HashMap;

fn main() {
    let mut map: HashMap<i32, i32> = HashMap::new();
    map.insert(0, 0);

    (1..5).for_each(|x| {
        let prev = map.get(&(x - 1)).unwrap();
        map.insert(x, (prev + 1) * 10);
    });

    map.iter().for_each(|(k, v)| println!("{} -> {}", k, v));
}

可以通过使用clone()作为prev来解决问题

let prev = map.get(&(x - 1)).unwrap().clone();

并获得以下输出:

0 -> 0
1 -> 10
2 -> 110
3 -> 1110
4 -> 11110

该怎么办才能不使用clone()

1 个答案:

答案 0 :(得分:2)

HashMap::get()返回Option<&V>,您将其解开,得到&V,因此最终得到prev是指向map内部的不变引用。这就是为什么您拥有map不变的借用的原因。 prev的类型为&i32

要解决您的问题,您可以取消引用该引用:

let prev = *map.get(&(x - 1)).unwrap();

在这种情况下,上一个的类型为i32,它只是堆栈上的一个值。它不会指向map =>内部,您不会一成不变地借用map,因此可以在下一行中一无所用地借用map

由于non-lexical lifetimes support,如果启用Rust 2018版,您的原始代码就可以正常工作。

请注意,HashMap未排序,因此输出行以任意顺序产生。如果需要排序,可以考虑使用BTreeMap

如果您根本不想使用Copy(通过*)或Clone,并且不想切换到Rust 2018,则可以执行以下操作:

let value_to_insert = {
    let prev = map.get(&(x - 1)).unwrap();
    (prev + 1) * 10
}
map.insert(x, value_to_insert)

这与非词汇生命周期在Rust 2018中会自动为您完成的事情一样。