为什么在过滤 Vec 时会得到双重引用?

Ing*_*ngo 1 rust

我总是在编写正确的迭代时遇到困难Vec。也许这是因为我还没有正确理解何时以及为什么引入引用。

例如

pub fn notInCheck(&self) -> bool { .... }       // tells whether king left in check
pub fn apply(&self, mv: Move) -> Self { ... }   // applies a move and returns the new position
pub fn rawMoves(&self, vec: &mut Vec<Move>) { ... } // generates possible moves, not taking chess into account

/// List of possible moves in a given position.
/// Verified to not leave the king of the moving player in check.
pub fn moves(&self) -> Vec<Move> {
    let mut vec: Vec<Move> = Vec::with_capacity(40);
    self.rawMoves(&mut vec);
    vec.iter().filter(|m| self.apply(**m).notInCheck()).map(|m| *m).collect()
}
Run Code Online (Sandbox Code Playgroud)

在哪里

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(transparent)]
pub struct Move {
    mv: u32,
}
Run Code Online (Sandbox Code Playgroud)

我首先写的迭代是:

vec.iter().filter(|m| self.apply(m).notInCheck()).collect()
Run Code Online (Sandbox Code Playgroud)

但是,当然,编译器给出了各种错误。在解决这些错误时,我最终得到了上面显示的版本,但是虽然编译器很高兴,但我不确定我是否满意。

看起来矢量根本不包含移动,而只是对移动的引用?但是,Move 的存储位置在哪里?此外,该filter()函数增加了另一个间接级别。这样对吗?请给我解释一下!

额外问题:当我有一个类型为实现 的向量元素时Copy,有没有办法避免所有这些无用的引用获取内容。我理解对于不想复制的显着大小的向量元素有何意义。但是,我绝对想避免&&valuefilter(). 我可以吗?

Mas*_*inn 5

看起来矢量根本不包含移动,而只是对移动的引用?但是,Move 的存储位置在哪里?此外,filter() 函数添加了另一个间接级别。这样对吗?请给我解释一下!

不,Vec<Move>绝对有动作。您缺少的部分是,除了filter()获取对迭代器项的引用之外,slice::iter还会在切片(或此处的 vec)项的引用上创建一个迭代器,因此Vec<Move>-> Iterator<Item=&Move>-> filter(predicate: FnMut(&&Move) -> bool),这就是为什么您有两个间接引用filter打回来。

当我有一个类型实现 Copy 的向量元素时,有没有办法避免所有这些无用的引用内容。我理解对于一个不想复制的显着大小的向量元素是如何有意义的。但是,我绝对想避免在 filter() 中使用 &&value。我可以吗?

是的。您可以使用into_iterwhich 将消耗源向量但直接迭代包含的值,或者您可以使用Iterator::copiedCopy迭代器项目的适配器,因此从Iterator<Item=&T>Iterator<Item=T>。然而filter永远不会得到T,它可以得到的最多的是一个&T因为否则项目将得到“迷失”(它会被过滤器,它只会返回一个布尔值消耗,产生......没什么用处)。

另一种方法是使用类似filter_mapwhich 确实获得T输入并返回Option<U>. 因为(顾名思义)它既过滤又映射,它可以使用输入项并返回输出项(可能相同)或返回“无”并从集合中删除该项。

顺便说一句,还有一个Iterator::cloned适用于类型的适配器,Clone但不是(必须)Copy

此外,您基本上可以通过在原始文件中翻转mapfilter左右来手动完成:

    vec.iter().map(|m| *m).filter(|m| self.apply(*m).notInCheck()).collect()
Run Code Online (Sandbox Code Playgroud)

map将 theIterator<Item=&T>转换为 an Iterator<Item=T>,然后filter只得到 an&T而不是 an &&T

除此之外,我真的不明白为什么apply需要消耗输入移动。或者为什么rawMoves不只是……在内部创建向量并返回它?我得到了允许重用缓冲区的优化,但这似乎是一种过早优化的情况吗?

而你的 Move 似乎……既过于复杂又有点过于简单?如果您只想新建一个 u32,那么使用元组结构似乎就足够了。

并且repr(transparent)完全没有必要,这只是在 FFI 上下文中的一个问题,其中 newtype 旨在作为仅存在于 Rust 端的类型安全措施(也就是 newtype 本身对 C可见/暴露于 C,只有包装类型是) .