为什么find闭包的参数需要两个&符号?

时间:2015-11-28 11:58:45

标签: reference rust

我一直在使用porting my Score4 AI engine来使用Rust - 基于我在OCaml中的功能样式实现。我特别想看看Rust如何使用功能风格的代码。

最终结果:它有效,并且速度非常快 - 比OCaml快得多。它几乎触及了命令式C / C ++的速度 - 这非常酷。

但是有一件事让我感到困扰 - 为什么我在这段代码的最后一行需要两个和号?

let moves_and_scores: Vec<_> = moves_and_boards
    .iter()
    .map(|&(column,board)| (column, score_board(&board)))
    .collect();
let target_score = if maximize_or_minimize { 
    ORANGE_WINS 
} else { 
    YELLOW_WINS 
};
if let Some(killer_move) = moves_and_scores.iter()
    .find(|& &(_,score)| score==target_score) {
         ...

我添加它们是因为编译错误&#34;指导&#34;我,它;但是我试图理解为什么......我使用Stack Overflow中其他地方提到的技巧来问&#34;问&#34;编译器告诉我什么类型的东西:

let moves_and_scores: Vec<_> = moves_and_boards
    .iter()
    .map(|&(column,board)| (column, score_board(&board)))
    .collect();
let () = moves_and_scores;

...导致此错误:

src/main.rs:108:9: 108:11 error: mismatched types:
 expected `collections::vec::Vec<(u32, i32)>`,
    found `()`
(expected struct `collections::vec::Vec`,
    found ()) [E0308]
src/main.rs:108     let () = moves_and_scores;

...正如我所料,moves_and_scores是元组的向量:Vec<(u32, i32)>。但是,在下一行中,iter()find()迫使我在闭包参数中使用可怕的双&符号:

if let Some(killer_move) = moves_and_scores.iter()
    .find(|& &(_,score)| score==target_score) {

为什么find闭合需要两个&符号?我可以看到为什么它可能需要一个(通过引用传递元组以节省时间/空间)但为什么两个?是因为iter吗?也就是说,是iter创建引用,然后find期望每个输入都有一个引用,所以参考引用?

如果是这样的话,这可能是Rust的一个相当难看的设计缺陷吗?

事实上,我希望findmap以及所有其他功能原语都是集合本身的一部分。迫使我iter()进行任何类型的功能性工作似乎很麻烦,如果它迫使这种&#34;双&符号&#34;在每一个可能的功能链中。

我希望我遗漏了一些明显的东西 - 任何帮助/澄清都是最受欢迎的。

1 个答案:

答案 0 :(得分:14)

这里

moves_and_scores.iter()

借用向量元素提供迭代器。如果您遵循API doc这是什么类型,您会注意到它只是借用切片的迭代器,并且Iterator实现了Item=&T,其中T(u32, i32)在你的情况下。

然后,使用find,它采用以&Item为参数的谓词。在您的情况下,Sice Item已经是引用,谓词必须采用&&(u32, i32)

pub trait Iterator {
    ...
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
    where P: FnMut(&Self::Item) -> bool {...}
    ...            ^

它可能被定义为this,因为它只应该检查项目并返回一个bool。这不需要按值传递项目。

如果你想要一个超过(u32, i32)的迭代器,你可以写

moves_and_scores.iter().cloned()
如果cloned()为{{{},则{p> Item将迭代器从&T类型Item转换为T类型T的迭代器1}}。另一种方法是使用Clone代替into_iter()

iter()

两者之间的区别在于第一个选项克隆了借来的元素,而第二个选项使用了向量并将元素移出了它。

通过写这样的lambda

moves_and_scores.into_iter()

您解构“双引用”并创建|&&(_, score)| score == target_score 的本地副本。这是允许的,因为i32是一个i32的简单类型。

您可以编写

,而不是解构谓词的参数
Copy

因为点运算符会根据需要自动解除引用次数。