我一直在使用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的一个相当难看的设计缺陷吗?
事实上,我希望find
和map
以及所有其他功能原语都是集合本身的一部分。迫使我iter()
进行任何类型的功能性工作似乎很麻烦,如果它迫使这种&#34;双&符号&#34;在每一个可能的功能链中。
我希望我遗漏了一些明显的东西 - 任何帮助/澄清都是最受欢迎的。
答案 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
因为点运算符会根据需要自动解除引用次数。