为什么不应该在HashSet::insert中使用&?

huangapple go评论61阅读模式
英文:

Why shouldn't we use & in HashSet::insert?

问题

我学习了C++之后学习Rust。想要理解为什么在HashSet::insert()函数中不应该使用&

我们在HashSet::contains()函数中使用&,因为我们不想拥有对象。我期望在insert()中也是相同的情况。

我正在阅读文档https://doc.rust-lang.org/std/collections/struct.HashSet.html#method.insert,并理解方法的签名pub fn insert(&mut self, value: T) -> bool。为什么不使用&value

我的代码

use std::collections::HashSet;

fn main() {
    let mut hset = HashSet::new();
    let num = 111;

    hset.insert(num);
    if hset.contains(&num) {
        println!("Contains {}", num);
    }

    println!("{:?}", hset);
}
英文:

I am learning Rust after C++. Want to understand why shouldn't we use & in HashSet::insert() function?

We are using & in HashSet::contains() function because we don't want to own object
I would expect the same for the insert()

I was reading documentation https://doc.rust-lang.org/std/collections/struct.HashSet.html#method.insert and understand signature of the method pub fn insert(&mut self, value: T) -> bool
Why don't we use &value ?

My code

use std::collections::HashSet;


fn main() {
    let mut hset = HashSet::new();
    let num = 111;

    hset.insert(num);
    if hset.contains(&num) {
        println!("Contains {}", num);
    }

    println!("{:?}", hset);
} 

答案1

得分: 4

让我尝试使用一个比喻:你的 HashSet 就像一个袋子,而你的 T 是可以放入这个袋子的东西的类型。你拥有这个袋子,并负责处理我的请求。

所以现在当我使用 HashSet::contains() 时,我拿着一个对象,比如一个红色的球,并问你:袋子里有类似的对象吗? 请注意,这个球是我的,但我让你借用它,这样你可以用它来与袋子里的对象进行比较。当你完成后,你将归还借用的对象,并回答 "是" 或 "否"。

但是当我使用 HashSet::insert() 时,我把球给了你,然后你把这个完全相同的球放入袋子中。如果袋子里已经有类似的对象,那么你会丢弃新的球(你不想在袋子里有重复的对象!),并回答 "否"。否则,你会把球放入袋子中,并回答 "是"。请注意,在任何情况下,现在我都不再拥有这个球了。

因此,在 contains() 情况下,你传递了一个引用,带有 &,因为你只是借用对象来使用一段时间。但在 insert() 中,你传递了对象本身作为拥有的值,以便容器能够拥有它。

英文:

Let me try to use a metaphor: your HashSetis a bag, and your T is the type of things you can put into that bag. You own the bag and are responsible to run my requests.

So now when I use HashSet::contains() I am holding an object, for example a red ball, and I ask you: Is there a similar object in the bag? Note that the ball is mine, but I let you borrow it so you can use it to compare to the objects inside the bag. When you are done you return the borrowed object and reply with "yes" or "no".

But when I use HashSet::insert() I give you the ball, and you insert that very same ball into the bag. If a similar object is already in the bag, then you trash the new ball (you do not want duplicated objects in your bag!) and reply "no". Else you insert the ball into the bag and reply "yes". Note that in either case now I do not have the ball any more.

So in the contains() case you pass a reference with & because you are borrowing the object to be used for a while. But with insert() you pass the object itself as an owned value so that the container is able to own it.

答案2

得分: 1

以下是翻译好的部分:

在许多情况下,您将希望 HashSet 拥有 其包含的值。在这些情况下,您只需直接传递值,即在调用 insert() 时将它们 移动

然而,上面的内容并不意味着您不能存储对象的引用。如果您调用 hset.insert(&num),编译器将推断 HashSet 的类型大致为 HashSet<&i32>(如果 num 的类型为 ì32)。

您可以使用以下代码进行测试:

use std::collections::HashSet;

fn main() {
    let mut hset = HashSet::new();
    let num: i32 = 111;
    hset.insert(&num);
    hset.aaaaa();
}

编译器会告诉您:

error[E0599]: no method named `aaaaa` found for struct `HashSet` in the current scope
     --> src/main.rs:7:10
      |
    7 |     hset.aaaaa();
      |          ^^^^^ method not found in `HashSet<&i32>`

顺便说一下,在上面的示例中,我明确将 num 的类型设置为 ì32。如果我们没有指定类型,编译器的输出可能在消息中更加通用,但仍然足够具体:HashSet<&{integer}>(也就是说,它仍然可以推断您正在尝试存储对某个整数的 引用)。

请注意,对于简单的类型如 i32,很少需要存储对这些值的引用,因为复制该值通常更高效。但如果您想存储字符串,情况就有所不同。对于短字符串,克隆通常是一种简单且足够快速的方法。但对于较长的字符串,您可能希望避免克隆,而改为使用 HashSet<&str>,但这样可能需要额外考虑/处理所有权和生命周期相关的事项。

英文:

In many cases you will want the HashSet to own the values it contains. In those cases you just want to pass the values directly, i.e. to move them, when calling insert().

However, the above doesn't mean you can't store references to objects instead. If you call hset.insert(&num), the compiler will infer the HashSet type as something like HashSet<&i32> (if num's type is ì32).

You can test that with this code:

use std::collections::HashSet;

fn main() {
    let mut hset = HashSet::new();
    let num: i32 = 111;
    hset.insert(&num);
    hset.aaaaa();
}

the compiler will tell you this:

error[E0599]: no method named `aaaaa` found for struct `HashSet` in the    current scope
 --> src/main.rs:7:10
  |
7 |     hset.aaaaa();
  |          ^^^^^ method not found in `HashSet<&i32>`

BTW in the above example I explicitly set num's type to be ì32. If we didn't specify that, the compiler's output might be a bit more generic in its message, but still specific enough: HashSet<&{integer}> (i.e., it can still infer you're trying to store a reference to some integer).

Note that for simple types like i32 there's rarely a need to store references to such values, since making a copy of the value is usually a lot more efficient. If you wanted to store strings though, it depends. For short strings cloning those is usually a simple and fast enough approach. For longer string you might want to avoid cloning by using HashSet<&str> instead, but then you might need some extra consideration/work w.r.t. ownership and lifetimes.

huangapple
  • 本文由 发表于 2023年5月7日 17:16:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193049.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定