我正在努力学习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)
这里,显然不需要在任何地方解引用vector v,但是我们需要解引用其他几个变量,例如第75行中的i,我不明白为什么。
1条答案
按热度按时间h7appiyu1#
说明这一点的最简单方法是在示例代码
x.foo(y)
的上下文中。x
这里是 * 接收器。* 给定x
的类型,编译器必须弄清楚foo
是什么函数。它试图在x
的类型上定位foo
,另外还通过 * 自动解引用 *x
,这就是您所询问的。此过程考虑x
的类型,以及通过尽可能多地取消引用x
遇到的每个类型。(它还考虑了这些类型的&
和可能的&mut
变体,这取决于x
或其引用类型的可变性。如果恰好找到一个
foo
,则调用被转换为T::foo(z, y)
,其中T
是foo
方法所在的类型,z
是任何解引用序列(加上最后一个可选的&
或&mut
)会根据T::foo
的要求生成T
、&T
或&mut T
。那么
y
呢?争论经历了“强制”,这完全是另一回事。链接的文档列出了所有可能的强制转换,为了简洁起见,我将省略它们,但值得注意的是没有&T
到T
和&mut T
到T
,其中一个是使map.entry(i)
在示例代码中工作所必需的。因此需要显式解引用。请注意,在强制执行列表中存在的项目之一是:
&T
或&mut T
到&U
(如果T
实现Deref<Target = 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
自动取消引用。但是,总的观点仍然成立--您不能将自动取消引用与取消引用强制合并。这是一个例子,他们会碰巧做同样的事情,但在许多其他情况下,他们不会。