为什么借用的HashMap的键和值是通过引用而不是值访问的?

3

我有一个函数,它接受了一个借用的 HashMap,我需要通过键访问值。为什么键和值是按引用传递而不是按值传递呢?

我的简化代码:

fn print_found_so(ids: &Vec<i32>, file_ids: &HashMap<u16, String>) {
    for pos in ids {
        let whatever: u16 = *pos as u16;
        let last_string: &String = file_ids.get(&whatever).unwrap();

        println!("found: {:?}", last_string);
    }
}
  • 为什么我必须将键指定为引用,即使用 file_ids.get(&whatever).unwrap() 而不是 file_ids.get(whatever).unwrap()

  • 据我所知,last_string 必须是 &String 类型,即 借用字符串,因为拥有该集合的是借用状态。这样理解对吗?

  • 与上面一点类似,我是否正确地认为 pos 的类型是 &u16,因为它从 ids 中获取了借用值?


好的,明白了。作为来自其他开发领域的人,有时候这个符号&对我来说更清晰一些。 - stej
3个回答

9

考虑将参数作为引用或值传递的语义:

  • 作为引用:没有所有权转移。被调用的函数仅借用参数。

  • 作为值:被调用的函数拥有参数的所有权,并且调用者不能再使用它。

由于函数 HashMap::get 不需要拥有键的所有权来查找元素,因此选择了更不限制的传递方法:通过引用。

此外,它不返回元素的值,只返回一个引用。如果它返回值,那么 HashMap 内部的值将不再属于 HashMap,因此将来将无法访问。


简而言之:因为它最便宜。 - Matthieu M.
明白了,谢谢。最让我困惑的是 &whatever。我本来以为简单的 u16 可以通过值进行复制(这甚至可能比将 64 位值作为指针复制更高效)。 - stej
@stej 一个 u16 可以通过值进行复制。然而,由于 HashMap 是泛型的,它被构建为对其他不可复制类型有用。如果需要,您可以始终复制/克隆引用以获取 u16 - Shepmaster

4

简而言之:Rust不是Java。

Rust可能具有高级结构和数据结构,但本质上它是一种低级语言,正如其指导原则之一所示:“你不必为你不使用的付出代价”。

因此,该语言及其库将尽可能地试图消除任何不必要的成本,例如不必要地分配内存。

案例1: 按值获取键。

如果键是String,这意味着为每个查找分配(和释放)内存,当你可以使用仅分配一次的本地缓冲区。

案例2: 按值返回。

按值返回意味着:

  • 您从容器中删除条目以将其提供给用户。
  • 您复制容器中的条目以将其提供给用户。

后者显然效率低下(复制意味着分配),前者意味着如果用户想要在另一个插入中将值返回,则必须再次进行插入,这意味着查找等等...同样效率低下。

总之,在这种情况下按值返回是低效的。

因此,就效率而言,Rust在实际操作中传递和按值返回。


谢谢您的评论。我已经多次阅读有关所有权的内容,但有时候它并没有完全理解;因此更多的信息是很有用的。 - stej

3

虽然当键是时,这似乎不太有用,但思考一下如果键更复杂,比如一个String

在这种情况下,通过值获取键通常意味着每次查找都需要分配和初始化一个新的String,这将是昂贵的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接