Rust 从迭代器中收集哈希图

aut*_*eps 6 iterator tuples hashmap rust

我们有一个 HashMap,我们对其进行迭代和映射以替换值,但是遇到了一个问题,将其收集回具有不同值类型的新 HashMap。

value of type `std::collections::HashMap<std::string::String, std::string::String>`
cannot be built from `std::iter::Iterator<Item=(&std::string::String, std::string::String)>`
Run Code Online (Sandbox Code Playgroud)

我们所做的基本上归结为:

let old: HashMap<String, Value> = some_origin();
let new: HashMap<String, String> = old.iter().map(|(key, value)| {
  return (key, some_conversion(value));
}).collect();
Run Code Online (Sandbox Code Playgroud)

如果对两个迭代器进行压缩,例如在这种情况下压缩键,并且映射仅返回转换后的值,则也会返回相同的迭代器类型(并且不可收集)。

new = old.keys().into_iter().zip(old.iter().map(|(key, value)| some_conversion(value)).collect();
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 8

问题是iter()docs)返回一个“非消耗”迭代器,它分发对基础值([1])的引用。newHashMap不能使用引用 ( &String)构造,它需要值 ( String)。

在您的示例中,some_conversion似乎String为 value 部分返回了一个 new ,因此应用.clone()到 key 可以解决问题:

let old: HashMap<String, Value> = some_origin();
let new: HashMap<String, String> = old.iter().map(|(key, value)| {
  return (key.clone(), some_conversion(value));
  //         ^---- .clone() call inserted
}).collect();
Run Code Online (Sandbox Code Playgroud)

是 Rust 操场上完整示例的链接。

看编译器的错误信息[2],这确实很难弄清楚。我认为对此最有帮助的是在 Rust 中建立关于引用和所有权的直觉,以了解何时引用是可以的以及何时需要拥有的值。

虽然我建议阅读 Rust Book 中关于引用和所有权的部分,甚至更多的 Programming Rust,但要点如下:

  1. 通常,Rust 中的值只有一个所有者(例外是显式共享所有权指针,例如Rc)。
  2. 当一个值被“按值”传递时,它被移动到新位置。这会使值的原始所有者无效。
  3. 一个值可以有多个共享引用,但是当存在任何共享引用时,该值是不可变的(不能存在或创建可变引用,因此不能修改或移动它)。
  4. 我们不能将值从共享引用中移出(这会使原始所有者无效,当共享引用存在时,该所有者是不可变的)。
  5. 通常,即使可以,Rust 也不会自动复制(用 Rust 的说法是“克隆”)值。相反,它需要价值的所有权。(例外是Copy复制成本低的“ ”类型,例如i32)。
  6. (此处不相关)也可以有一个对值的可变引用。当这个可变引用退出时,不能创建共享引用。

这有什么帮助?

  • 谁拥有哈希映射中的键?哈希映射可以(规则 1)!
  • 但是我们如何将新的键值对放入哈希映射中呢?这些值被移动到哈希映射中(规则 2)。
  • 但是我们不能移出共享引用...(规则 3 + 规则 4)
  • 而且 Rust 不想克隆值,除非我们告诉它这样做(规则 5)
  • ...所以我们必须自己克隆它。

我希望这能提供一些直觉(我再次真正推荐 Rust 编程)。一般来说,如果你做某事具有价值,你要么拥有它的所有权,要么得到一个参考。如果您取得所有权,则无法再使用拥有所有权的原始变量。如果您获得参考,则不能将所有权交给其他人(无需克隆)。而 Rust 不会为你克隆。

[1]:文档称其为“以任意顺序访问所有键值对的迭代器。迭代器元素类型为 (&'a K, &'a V)”。忽略'a生命周期参数,您可以看到元素类型为(&K, &V)

[2]:

13 |         .collect();
   |          ^^^^^^^ value of type `std::collections::HashMap<std::string::String, std::string::String>` cannot be built from `std::iter::Iterator<Item=(&std::string::String, std::string::String)>`
   |
   = help: the trait `std::iter::FromIterator<(&std::string::String, std::string::String)>` is not implemented for `std::collections::HashMap<std::string::String, std::string::String>`
Run Code Online (Sandbox Code Playgroud)