我在section 8.3 of The Book年为第三个提议的练习写了一个可行的解决方案,但其中一些行为违背了我的直觉.具体地说,我似乎能够改变一个似乎实例化为不可变的向量.

我已经包括了我认为相关的代码部分,省略了与HashMap中存储的Vec不交互的代码部分.

我在代码块之后进行了一些猜测,但我真的需要一个更可靠的解释来说明实际发生了什么.

// Start by declaring a new HashMap. This is where the mystery begins for me.
let mut departments: HashMap<String, Vec<String>> = HashMap::new();

// This code deals with getting user input from stdin and has been elided
// ...

// Match the first string slice
match args[0] {
  
  // Add an employee to a department in the HashMap
  "add" => {
    
    // POINT OF INTEREST 1
    // Adding a department that isn't already in the HashMap will cause a new empty
    // vector to be inserted at the newly created key
    let list = departments.entry(args[2].to_string()).or_insert(Vec::new());

    // In any case, we insert the employee's name into the vector associated with
    // whatever department we got from stdin
    list.push(args[1].to_string());
  }

  // List employees
  "who" => match args[1] {

    // List all employees in every department, employees shown in alphabetical order
    "all" => {
      for (department, employees) in &mut departments {

        // POINT OF INTEREST 2
        // Why am I allowed to sort this vector? The artifact underlying the reference
        // was never explicitly made mutable.
        employees.sort();

        for ee in employees {
          println!("{}: {}", department, ee);
        }
      }
    }

    // List all employees in a given department, employees shown in alphabetical order
    dept => {
      let employees = departments.get_mut(dept);

      match employees {
        Some(entries) => {

          // POINT OF INTEREST 3
          // This one is seems the least mysterious to me, since I get the value
          // of the HashMap at `dept` through `.get_mut()`.
          println!("{}:", dept);
          entries.sort();

          for ee in entries {
            println!("\t{}", ee);
          }
        }
        _ => (),
      }
    }
  }
}

Hypothesis 1:at POINT OF INTEREST 1,我对.or_insert()returns a mutable reference的调用是对一个新向量的调用,而this是后来对HashMap中值的.sort()值进行调用的原因.

这看起来不像是可能的答案!在开始时,我声明departments是类型HashMap<String, Vec<String>>,not,HashMap<String, &mut Vec<String>>.

Hypothesis 2:当我将departments声明为可变时,它的键和值将继承这种可变性.这似乎不太可能,因为在我(非常有限的)经验中,没有任何东西表明这样的事情是铁 rust 的一个特征.我还认为,如果这本书的前8章明确说明了这一点,我就会注意到这一点,但我以前就知道会略过重要的细节.

推荐答案

for (department, employees) in &mut departments {

for循环利用this IntoIter implementation:

impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S> {
    type Item = (&'a K, &'a mut V);
}

由于这种实现,当您在&mut HashMap<K, V>上迭代时,您会得到(&K, &mut V)的元组.请注意,密钥是不变的,而值是可变的.这使您可以修改这些值,因为employees的类型为&mut Vec<String>.

为什么映射能够返回可变引用?映射同时拥有键和值,因此如果愿意,它可以返回对其中任何一个的可变引用.这就是作为所有者的意义所在:如果你愿意,你可以让别人随意borrow 你的物品.

HashMap很乐意让您改变值,因为这不会影响数据 struct .它不允许您修改键,因为这会更改它们的散列并使它们在散列表中的存储位置无效.HashMap could返回&mut K.借阅判断员不会阻止它的.然而,它不会,因为调用者可能会 destruct 散列映射.

Rust相关问答推荐

为什么拥有的trait对象的相等运算符移动了正确的操作数?

当为a Self:IntoIterator设置trait bind `时,获取`a T `不是迭代器"&'"<'>&'

Rust中的相互递归特性与默认实现

为什么我可以跟踪以前borrow 过的变量?房主在哪里?

修改切片/引用数组

从Type::new()调用函数

如何编写一个以一个闭包为参数的函数,该函数以另一个闭包为参数?

我如何使用AWS SDK for Rust获取我承担的角色的凭据?

Rust将String上的迭代器转换为&;[&;str]

应为关联类型,找到类型参数

在运行特定测试时,如何 suppress cargo test 的空输出?

如何使用reqwest进行异步请求?

Rust 中的复合 `HashSet` 操作或如何在 Rust 中获得 `HashSet` 的显式差异/并集

如何限制 GtkColumnView 行数

unwrap 选项类型出现错误:无法移出共享引用后面的*foo

闭包返回类型的生命周期规范

Rust 中 Mutex<> 的深拷贝?

如何异步记忆选项中的 struct 字段

匹配结果时的简洁日志(log)记录

如何迭代调用可能会失败的函数?操作员?