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

tts*_*ras 11 reference rust

通过将我的Score4 AI引擎移植到它,我一直在玩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) {
         ...
Run Code Online (Sandbox Code Playgroud)

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

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

...导致此错误:

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;
Run Code Online (Sandbox Code Playgroud)

......正如我所料,moves_and_scores是一个元组的向量:Vec<(u32, i32)>.但随后,在不久的下一行,iter()find()强迫我用可怕的双&符号在封闭参数:

if let Some(killer_move) = moves_and_scores.iter()
    .find(|& &(_,score)| score==target_score) {
Run Code Online (Sandbox Code Playgroud)

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

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

实际上,我希望find并且map所有其他功能原语都是集合本身的一部分.迫使我iter()去做任何类型的功能式工作似乎很麻烦,如果它在每个可能的功能链中强制使用这种"双符号",那就更是如此.

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

sel*_*tze 14

这里

moves_and_scores.iter()
Run Code Online (Sandbox Code Playgroud)

给你一个借来的向量元素的迭代器.如果按照API文档是什么类型,这是,你会发现它只是借来的片迭代器,这实现了IteratorItem=&T地方T(u32, i32)在你的案件.

然后,您使用find带有&Itemas参数的谓词.Item在你的情况下,Sice 已经是一个参考,谓词必须采用&&(u32, i32).

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

它可能是这样定义这个,因为它应该只是一个检查项目,并返回一个布尔值.这不需要按值传递项目.

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

moves_and_scores.iter().cloned()
Run Code Online (Sandbox Code Playgroud)

cloned()从一个迭代转换用Item类型&T的一个与Item类型T,如果TClone.另一种方法是使用into_iter()而不是iter().

moves_and_scores.into_iter()
Run Code Online (Sandbox Code Playgroud)

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

通过写这样的lambda

|&&(_, score)| score == target_score
Run Code Online (Sandbox Code Playgroud)

你解构"双引用"并创建一个本地副本i32.这是允许的,因为它i32是一种简单的类型Copy.

您也可以编写,而不是解构谓词的参数

|move_and_score| move_and_score.1 == target_score
Run Code Online (Sandbox Code Playgroud)

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

  • @ttsiodras你可以通过许多不同的方式迭代一个集合(`iter`,`iter_mut`,`into_iter`,`drain`).每个都有它们的用途,并没有真正的"正确"方法来做到这一点.我猜,这就是为什么你必须具体. (2认同)