【问题标题】:Get value from map and modify map in same scope [duplicate]从地图中获取价值并在同一范围内修改地图[重复]
【发布时间】:2019-03-01 20:38:33
【问题描述】:

这是一个简单的程序,它在map 中查找先前的值并将新值放入其中。它不会编译,因为第一个查找不可变地借用 mapinsert 想要可变地借用 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() on 得到prev as

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

并得到如下输出:

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

我该怎么做才能不在这里使用clone()

【问题讨论】:

  • 如果不使用 NLL,请使用范围,play.integer32.com/…
  • 您提供的代码compiles as-is。升级您的 Rust 版本并切换到 Rust 2018。
  • @Stargateur 能否请您将此作为答案发布,以便我将其标记为已接受的答案。
  • @Stargateur 发布的片段清楚地解释了词法生命周期如何帮助清楚地理解每个变量的使用。 Non Lexical Lifetimes (NLL) 确实有助于更轻松地编写代码,但这里的范围界定似乎是一个更好的想法和更惯用的恕我直言。

标签: rust


【解决方案1】:

HashMap::get() 返回Option&lt;&amp;V&gt;,你打开它,得到&amp;V,所以你最终得到prev 是一个指向map 内部的不可变引用。这就是为什么你有一个不可变的借用 mapprev 的类型是&amp;i32

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

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

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

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

请注意,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 中非词法生命周期自动为您做的事情几乎相同。

【讨论】:

  • 已更新。恕我直言,这对 i32 的价值毫无意义。
  • 使用* 操作符取消引用与clone() 相同,它不能回答问题。 @Stargateur 发布的代码 sn-p 实际上解决了它。
猜你喜欢
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 1970-01-01
  • 1970-01-01
  • 2019-11-10
  • 1970-01-01
相关资源
最近更新 更多