Rust 地图上的max#U by#U键不';不允许将元组分解为键值对

Rust 地图上的max#U by#U键不';不允许将元组分解为键值对,rust,lifetime,Rust,Lifetime,我正在学习锈迹,对所有权、借阅和参考资料的概念掌握得相当好。我已经读到了《铁锈书》第二版第8章 我正在使用map实现模式功能。我使用迭代器::max_by_key编写了以下实现: use std::collections::HashMap; fn main() { let vs = vec![0, 0, 1, 1, 3, 4, 5, 6, 3, 3, 3]; let mut counts = HashMap::new(); for num in vs {

我正在学习锈迹,对所有权、借阅和参考资料的概念掌握得相当好。我已经读到了《铁锈书》第二版第8章

我正在使用
map
实现
模式
功能。我使用
迭代器::max_by_key
编写了以下实现:

use std::collections::HashMap;

fn main() {
    let vs = vec![0, 0, 1, 1, 3, 4, 5, 6, 3, 3, 3];

    let mut counts = HashMap::new();
    for num in vs {
        let count = counts.entry(num).or_insert(0);
        *count += 1;
    }

    // This works
    let u = counts.iter().max_by_key(|v| v.1);

    // This doesn't work
    let v = counts.iter().max_by_key(|(k, v)| v);
}
我发现以下编译器错误

错误[E0495]:由于需求冲突,无法推断模式的适当生存期
-->src/main.rs:16:43
|
16 |设v=counts.iter().max_by_key(|(k,v)| v);
|                                           ^
|
注意:首先,生命周期不能超过16:38在身体上定义的匿名生命周期#2。。。
-->src/main.rs:16:38
|
16 |设v=counts.iter().max_by_key(|(k,v)| v);
|                                      ^^^^^^^^^^
注意:…这样引用就不会超过借用的内容
-->src/main.rs:16:43
|
16 |设v=counts.iter().max_by_key(|(k,v)| v);
|                                           ^
注意:但是,生命周期必须在16:13对方法调用有效。。。
-->src/main.rs:16:13
|
16 |设v=counts.iter().max_by_key(|(k,v)| v);
|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
注意:…因此类型/生存期参数在这里的作用域中
-->src/main.rs:16:13
|
16 |设v=counts.iter().max_by_key(|(k,v)| v);
|             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这个错误是什么意思?为什么不允许

更新1:解决了我的问题。如果我使用的是稳定的编译器,我就不会问这个问题了。这里我遇到了意外的编译错误,所以我不会以重复的形式关闭它。

简而言之,使用引用()


从长远来看,第一个示例是有效的,因为
v
是可复制的,这意味着您将在闭包中获得
v
的副本。元组是不可复制的,这意味着元组将从hashmap中移出,这是不允许的,这就是为什么您必须使用引用。

解决方案是添加单个
&

counts.iter().max_by_key(|&(k, v)| v);
//                        ^
。。。或者(在夜间)添加单个
*

counts.iter().max_by_key(|(k, v)| *v);
//                                ^
counts.iter().max_by_key(|(k, v)| *v);
下面是一个详细的解释,以及如何了解自己的说明。如果你没有时间,最后会有一个总结


那么,这是为什么? 为了找出答案,让我们首先分析这个片段中
x
的类型(这是您的第一个版本,但为了清楚起见,我将
v
重命名为
x
):

要检查
x
的类型,我们基本上有两种可能性:翻阅文档或让编译器告诉我们。让我们先浏览文档,然后用编译器确认这一点

所以
counts
是一个
HashMap
,其中
{integer}
只是一种整数:编译器仍然需要精确地计算出哪个整数。如果没有给出更具体的信息(如在您的示例中),编译器默认为整数的
i32
。为了让我们更容易,让我们修复整数类型:

let mut counts: HashMap<i32, u32> = HashMap::new();
现在,我们可以单击Iter以获取有关该类型的更多信息,也可以单击左侧的感叹号:

无论哪种方式,我们都看到了这一重要的含义:

impl<'a, K, V> Iterator for Iter<'a, K, V>
    type Item = (&'a K, &'a V);
这有点复杂,但别担心!我们看到该方法(除了
self
)需要一个参数
f:f
。这是你传递的结束语。
where
子句告诉我们
F:FnMut(&Self::Item)
意味着
F
是一个函数,它有一个类型为
&Self::Item
的参数

但是我们已经知道迭代器的
Self::Item
是什么:
(&i32,&u32)
。因此
&Self::Item
(带有添加的引用)是
&(&i32,&u32)
!这是闭包参数的类型,因此是
x
的类型

让我们检查一下我们的研究是否正确。通过强制执行类型错误,您可以很容易地指示编译器告诉您变量
x
的类型。让我们添加表达式
x==()
。在这里,我们尝试将您的变量与永远不起作用的
()
进行比较。事实上,我们得到了一个错误:

14 | x==();
|^^无法将`&(&i32,&u32)`与`()`
成功!我们正确地找到了
x
的类型。那么这对我们有什么帮助呢

在第二个示例中,您写道:

counts.iter().max_by_key(|(k, v)| v);
所以在闭包的参数列表中使用了模式匹配。但是有人可能会想:等等,编译器怎么能将模式
(k,v)
与类型
&(&i32,&u32)
相匹配呢?开头有一个引用不合适

这正是稳定编译器上发生的事情:

error[E0658]:用于匹配引用的非引用模式(参见问题42640)
-->src/main.rs:18:39
|
18 | counts.iter().max|u by|u键(|(k,v)| v);
帮助:考虑使用参考文献:‘(k,v)’
您可以看到模式
&(k,v)
确实适合
&(&i32,&u32)
(使用
k=&i32
v=&u32

因此,谈到稳定编译器,您的问题仅仅是您的模式不适合预期的类型

那么每晚的错误是怎么回事? 最近,一些符合人体工程学的改进落在了Rust上(仍仅限夜间),这有助于减少常见情况下的噪音代码。这项特别的改进是在年提出的。这种常见情况是匹配元组的引用,并希望获得对元素的引用,就像在本例中匹配类型
&(bool,String)

因此,如果不考虑引用,您可能会使用模式
(b,s)
(类似于您使用
(k,v)
)。但这不起作用(在稳定状态下),因为模式起作用
impl<'a, K, V> Iterator for Iter<'a, K, V>
    type Item = (&'a K, &'a V);
fn max_by_key<B, F>(self, f: F) -> Option<Self::Item> 
where
    B: Ord,
    F: FnMut(&Self::Item) -> B, 
counts.iter().max_by_key(|(k, v)| v);
match &(true, "hi".to_string()) {
    // ...
}
counts.iter().max_by_key(|(k, v)| v);
counts.iter().max_by_key(|&(k, v)| v);
counts.iter().max_by_key(|(k, v)| *v);