英文:
How does automatic de-referencing work in Rust?
问题
我正在尝试学习Rust,并且有些困惑为什么在某些情况下需要解引用指针而在其他情况下不需要。在网上寻找答案时,我发现Rust具有自动解引用功能,但我找不到关于这个主题的详细信息。有人可以解释一下这是如何工作的吗?
为了更具体,这是我遇到这个问题的一个示例代码。
fn mode(v: &mut Vec<i32>) -> Option<i32> {
let mut map: HashMap<i32, i32> = HashMap::new();
let mut max = (0, 0);
if v.is_empty() {
return None;
} else {
();
}
for i in v {
let count = map.entry(*i).or_insert(0);
*count += 1;
}
for (k, v) in map.iter() {
if v > &max.1 {
max = (*k, *v);
} else {
();
}
}
Some(max.0)
}
在这里,显然没有必要解引用向量 v 的任何地方,但我们需要解引用其他多个变量,如第75行的 i,我不明白为什么需要这样做。
英文:
I am trying to learn Rust and am having some trouble understanding why we need to dereference pointers in some cases and not in others. Fishing for answers online, I found out that Rust has automatic dereferencing, but I couldn't find much on the topic. Can someone explain to me how this exactly works?
To make it more specific, here is a sample code where I encountered this problem.
fn mode(v: &mut Vec<i32>) -> Option<i32> {
let mut map: HashMap<i32, i32> = HashMap::new();
let mut max = (0, 0);
if v.is_empty() {
return None;
} else {
();
}
for i in v {
let count = map.entry(*i).or_insert(0);
*count += 1;
}
for (k, v) in map.iter() {
if v > &max.1 {
max = (*k, *v);
} else {
();
}
}
Some(max.0)
Here, there is apparantely no need to dereference vector v anywhere, but we need to dereference several other variables such as i in line 75, and I do not understand why.
答案1
得分: 4
以下是翻译好的内容:
最简单的方法来说明这一点是在示例代码x.foo(y)
的上下文中。
这里的x
是_接收者。鉴于x
的类型,编译器必须找出foo
是什么函数。它试图在x
的类型上定位foo
,并且通过_自动解引用_ x
,这就是你所询问的。这个过程考虑了x
的类型以及通过解引用x
尽可能多次遇到的每个类型。(它还考虑了&
和可能是&mut
这些类型的变体,这取决于x
或其引用类型的可变性。)
如果确切地找到一个foo
,那么调用将被转换为T::foo(z, y)
,其中T
是foo
方法所在的类型,而z
是任何一系列解引用(加上最后一个可选的&
或&mut
)的结果为T
、&T
或&mut T
,根据T::foo
的要求。
那么y
呢?参数经历强制类型转换,这是一个完全不同的事情。链接的文档列出了所有可能的强制类型转换,出于简洁起见,我将省略它们,但值得注意的是&T
到T
和&mut T
到T
都不包括在内,其中之一将需要使map.entry(i)
在你的示例代码中工作。因此,明确的解引用是必需的。
请注意,强制类型转换列表中包含的一个项是:
> 如果T
实现了Deref<Target = U>
,则&T
或&mut T
转换为&U
。
注意,这个操作实际上_不会解引用引用_,而是_将引用转换为另一种类型的引用!_也就是说,强制类型转换是&T
到&U
,而不是&T
到U
。这就是允许你将&String
传递给期望&str
的参数的原因,例如(String
实现了Deref<Target = str>
),但这个规则不提供从&mut i32
到i32
的强制类型转换,这是使map.entry(i)
工作所需的。
for i in v
是一个完全不同的情况;这起作用是因为&mut Vec<T>
实现了IntoIterator
。
因此,为什么你不必解引用v
的答案是因为你在接收者位置使用它,这里会发生自动解引用。函数参数则经历强制类型转换,具有完全不同的一套规则。
现在说到这一点,在你的代码中,v
已经是对Vec
的引用,因此v.is_empty()
也可以通过强制类型转换来解释,因为这个方法来自切片,而Vec
会自动解引用为切片。然而,总体观点仍然成立——你不能混淆自动解引用和解引用强制类型转换。这是一个例子,在其中它们恰好做同样的事情,但在许多其他情况下,它们不会这样做。
英文:
The easiest way to illustrate this is within the context of the sample code x.foo(y)
.
x
here is the receiver. Given the type of x
, the compiler has to figure out what function foo
is. It attempts to locate foo
on x
's type, and additionally by auto-dereferencing x
, which is what you are asking about. This process considers the type of x
as well as every type that is encountered by dereferencing x
as many times as possible. (It also considers &
and possibly &mut
variants of these types, depending on the mutability of x
or its reference type.)
If exactly one foo
is found, then the call is transformed to T::foo(z, y)
where T
is the type the foo
method was located on, and z
is whatever sequence of dereferences (plus one final, optional &
or &mut
) results in a T
, &T
, or &mut T
, as required by T::foo
.
What about y
, though? Arguments undergo coercion, which is a different thing altogether. The linked documentation lists all of the possible coercions, which I will omit for the sake of brevity, but notably absent are &T
to T
and &mut T
to T
, one of which would be required to make map.entry(i)
work in your sample code. The explicit dereference is therefore required.
Note that one of the items present in the list of coercions is:
> &T
or &mut T
to &U
if T
implements Deref<Target = U>
.
Observe that this doesn't actually dereference a reference but rather converts the reference to another type of reference! That is, the coercion is &T
to &U
, not &T
to U
. This is what allows you to give &String
to an argument that expects a &str
, for example (String
implements Deref<Target = str>
), but this rule doesn't provide coercion from &mut i32
to i32
as would be required to make map.entry(i)
work.
for i in v
is a completely different scenario; this works simply because &mut Vec<T>
implements IntoIterator
.
So the answer as to why you don't have to dereference v
is because you are using it in receiver position, where auto-dereference happens. Function arguments instead undergo coercion, which has an entirely different set of rules.
Now having said that, in your code v
is already a reference to a Vec
, and so v.is_empty()
could be explained through coercion as well, as this method comes from slices, to which Vec
automatically dereferences. However, the overall point still stands -- you cannot conflate auto-dereferencing with deref coercion. This is one instance where they would happen to do the same thing, but in many other cases they would not.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论