【问题标题】:How can I update a value in a mutable HashMap?如何更新可变 HashMap 中的值?
【发布时间】:2015-08-05 12:32:50
【问题描述】:

这是我想要做的:

use std::collections::HashMap;

fn main() {
    let mut my_map = HashMap::new();
    my_map.insert("a", 1);
    my_map.insert("b", 3);

    my_map["a"] += 10;
    // I expect my_map becomes {"b": 3, "a": 11}
}

但这会引发错误:

Rust 2015

error[E0594]: cannot assign to immutable indexed content
 --> src/main.rs:8:5
  |
8 |     my_map["a"] += 10;
  |     ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
  |
  = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, i32>`

Rust 2018

error[E0594]: cannot assign to data in a `&` reference
 --> src/main.rs:8:5
  |
8 |     my_map["a"] += 10;
  |     ^^^^^^^^^^^^^^^^^ cannot assign

我不太明白这意味着什么,因为我将 HashMap 设置为可变的。当我尝试更新 vector 中的元素时,我得到了预期的结果:

let mut my_vec = vec![1, 2, 3];

my_vec[0] += 10;
println! {"{:?}", my_vec};
// [11, 2, 3]

HashMap 与我收到上述错误有何不同?有没有办法更新值?

【问题讨论】:

    标签: hashmap rust


    【解决方案1】:

    我将分享我自己的答案,因为我遇到了这个问题,但我正在使用 Structs,所以在我的情况下,这种方式有点棘手

    use std::collections::HashMap;
    
    #[derive(Debug)]
    struct ExampleStruct {
        pub field1: usize,
        pub field2: f64,
    }
    
    fn main() {
        let mut example_map = HashMap::new();
        &example_map.insert(1usize, ExampleStruct { field1: 50, field2: 184.0});
        &example_map.insert(6usize, ExampleStruct { field1: 60, field2: 486.0});
    
        //First Try
        (*example_map.get_mut(&1).unwrap()).field1 += 55; //50+55=105
        (*example_map.get_mut(&6).unwrap()).field1 -= 25; //60-25=35
    
        //Spliting lines
        let op_elem = example_map.get_mut(&6);
        let elem = op_elem.unwrap();
        (*elem).field2 = 200.0;
    
        let op_ok_elem = example_map.get_mut(&1);
        let elem = op_ok_elem.unwrap_or_else(|| panic!("This msg should not appear"));
        (*elem).field2 = 777.0;
    
        println!("Map at this point: {:?}", example_map);
        let op_err_elem = example_map.get_mut(&8);
        let _elem = op_err_elem.unwrap_or_else(|| panic!("Be careful, check you key"));
    
        println!("{:?}", example_map);
    }
    

    你可以在Rust Playground上玩这个

    【讨论】:

      【解决方案2】:

      考虑:

      let mut m = std::collections::HashMap::new();
      m.insert("a", 1);
      m.insert("b", 3);
      let k = "c";
      

      如果密钥已经存在:

          m.insert(k, 10 + m[k] );
      

      如果密钥不存在:

      1. 您可以更新键的值:
          m.insert(k, 10 + if m.contains_key(k) { m[k] } else { 0 });
      
      1. 或仅在密钥不存在时才插入密钥:
          m.entry(k).or_insert(0);
          m.insert(k, 200 + m[k]);
      
      1. 或者更新一个密钥,防止密钥可能没有被设置:
          *m.entry(k).or_insert(0) += 3000;
      

      最后打印值:

          println!("{}", m[k]); // 3210
      

      见:
      https://doc.rust-lang.org/std/collections/struct.HashMap.html

      【讨论】:

        【解决方案3】:

        不可变索引和可变索引由两个不同的特征提供:分别为IndexIndexMut

        目前HashMap没有实现IndexMut,而Vec does

        The commit that removed HashMap's IndexMut implementation 状态:

        此提交删除了 HashMap 和 BTreeMap 上的 IndexMut impls,在 为了防止 API 最终包含 IndexSet 特征。

        据我了解,假设的 IndexSet 特征将允许您为 HashMap 分配全新的值,而不仅仅是读取或改变现有条目:

        let mut map = HashMap::new();
        map["key"] = "value";
        

        目前,您可以使用get_mut

        *my_map.get_mut("a").unwrap() += 10;
        

        或者entry API:

        *my_map.entry("a").or_insert(42) += 10;
        

        【讨论】:

        • IndexMut 预计会在未来实施吗?
        • @LukeDupin 表示怀疑。取而代之的是假设的 IndexSet 特征。
        • *my_map.entry("a").or_insert(42) += 10; 那*有什么作用?取消引用哪一部分?
        • @Eftekhari 带括号:(*(my_map.entry("a").or_insert(42))) += 10;
        • @GermanFaller let tmp = my_map.entry("a").or_insert(some_default); tmp.a += 1; tmp.b += 2.
        猜你喜欢
        • 2012-01-28
        • 1970-01-01
        • 1970-01-01
        • 2020-06-26
        • 1970-01-01
        • 2021-04-11
        • 2016-09-12
        • 1970-01-01
        • 2016-02-07
        相关资源
        最近更新 更多