如何设置这些结构体的生命周期?

3

我正在尝试制作一个迭代器,用于从移动列表中过滤出某些移动。为了不占用返回的移动所有权,它们被引用。然而,在这样做时,会出现“missing lifetime specifier”编译器错误。由于我有一系列的结构体,我认为解决问题的第一步是开始在MoveFilter上放置生命周期,然后在IntoIterator中将其类型项Item上的生命周期加上。然而,它抱怨使用了未声明的生命周期。

pub struct GameMove {
    pub from: usize,
    pub to: usize,
    pub move_type: GameMoveType,
    pub piece_type: PieceType,
}
#[derive(PartialEq, Clone, Debug)]
pub enum GameMoveType {
    Quiet,
    Capture(PieceType),
}

#[derive(PartialEq, Clone, Debug)]
pub enum PieceType {
    King,
    Pawn
}

pub fn match_move_type(move_type: &GameMoveType) -> usize {
    match move_type {
        GameMoveType::Quiet => 0,
        GameMoveType::Capture(_) => 1,
    }
}

pub struct MoveFilter<'a> {
    legal_moves: Vec<GameMove>,
    move_type: GameMoveType,
}

impl IntoIterator for MoveFilter {
    type Item = &'a GameMove;
    type IntoIter = MoveFilterIterator;
    fn into_iter(self) -> Self::IntoIter {
        MoveFilterIterator {
            legal_moves: self.legal_moves,
            move_type: match_move_type(&self.move_type),
            index: 0,
        }
    }
}

pub struct MoveFilterIterator {
    legal_moves: Vec<GameMove>,
    move_type: usize,
    index: usize,
}

impl Iterator for MoveFilterIterator {
    type Item = &GameMove;
    fn next(&mut self) -> Option<&GameMove> {
        while self.index < self.legal_moves.len() {
            if match_move_type(&self.legal_moves[self.index].move_type) == self.move_type {
                Some(&self.legal_moves[self.index])
            } else {
                self.index += 1;
            }
        }
        None
    }
}

1
请注意,使用对GameMoveType的引用的另一种解决方案是仅为其派生“Copy”(这又将要求同样操作PieceType)。类型似乎非常轻量级,因此除非您当然已从MCVE的定义中剥离了某些昂贵的复制组件,否则Copy看起来是比较合适的。 - Michail
1个回答

2
你的方法 fn into_iter(self: MoveFilter) 接受了 MoveFilter 的所有权,而方法 fn next(&mut self) -> Option<&GameMove> 只想提供给 GameMove 的不可变引用。在你的 into_iter 获取 MoveFilter 的所有权并完全消耗它之后,谁应该拥有被引用的 GameMove 呢?
修复这个问题的一种方法是为 &'a MoveFilter 实现 IntoIterator,它不会获取 MoveFilter 的所有权,因此不必担心在存在任何引用 &'a GameMove 的情况下丢弃所有 GameMove
pub struct GameMove {
    pub from: usize,
    pub to: usize,
    pub move_type: GameMoveType,
    pub piece_type: PieceType,
}

#[derive(PartialEq, Clone, Debug)]
pub enum GameMoveType {
    Quiet,
    Capture(PieceType),
}

#[derive(PartialEq, Clone, Debug)]
pub enum PieceType {
    King,
    Pawn
}

pub fn match_move_type(move_type: &GameMoveType) -> usize {
    match move_type {
        GameMoveType::Quiet => 0,
        GameMoveType::Capture(_) => 1,
    }
}

pub struct MoveFilter {
    legal_moves: Vec<GameMove>,
    move_type: GameMoveType,
}

impl<'t> IntoIterator for &'t MoveFilter {
    type Item = &'t GameMove;
    type IntoIter = MoveFilterIterator<'t>;
    fn into_iter(self) -> Self::IntoIter {
        MoveFilterIterator {
            legal_moves: &self.legal_moves[..],
            move_type: match_move_type(&self.move_type),
            index: 0,
        }
    }
}

pub struct MoveFilterIterator<'a> {
    legal_moves: &'a [GameMove],
    move_type: usize,
    index: usize,
}

impl<'a> Iterator for MoveFilterIterator<'a> {
    type Item = &'a GameMove;
    fn next(&mut self) -> Option<&'a GameMove> {
        while self.index < self.legal_moves.len() {
            if match_move_type(&self.legal_moves[self.index].move_type) == self.move_type {
                return Some(&self.legal_moves[self.index])
            } else {
                self.index += 1;
            }
        }
        None
    }
}

另一个可能的解决方案是保留你的IntoIterator for MoveFilter不变,但将Item = &GameMove更改为Item = GameMove。这将给你一个破坏性迭代器,可以移动MoveFilter并且只能使用一次,但我认为这不是你在开始使用type Item = &GameMove时想要的结果。实现起来似乎有点棘手,因为从向量中删除单个元素并不完全简单,并且我并没有完全理解那个while循环在做什么。最初的回答。

while循环的作用是,如果当前索引处的移动类型与指定的移动类型不匹配,则继续执行下一个移动。 - Fabian v.d.W
1
@Fabianv.d.W 我想我只能相信你的话,因为我不知道你是如何对领域进行建模的,而且我也不太理解这个领域... 另外一点:&'a FooBarIntoIterator 是一个相当常见的习语,请查看一些标准库中的例子,大约有一半(?)看起来像 &'a SomethingIntoIterator - Andrey Tyukin
1
@Fabianv.d.W 这就是我将拥有 Vec<GameMove> 替换为不可变引用 &'a [GameMove] 的原因。 - Andrey Tyukin
1
@Fabianv.d.W 不,它不会。方法 fn into_iter(self) 接受一个 self: Self,而 Self 现在已经被更改为 Self = &'t MoveFilter 而不是 Self = MoveFilter。不要将其与拥有向量的 into_iter() 或类似的东西混淆:对于拥有向量,它会产生一个破坏性迭代器。但是,在这里,我为一个不可变引用 &'t MoveFilter 实现了它,因此 into_iter() 不会移动任何东西。 - Andrey Tyukin
1
@Fabianv.d.W 标记(如果您指的是(at)-thing)并不必要,因为我会收到所有在我的帖子下面发表的评论的通知(这通常是如何工作的)。在评论中使用行内代码的方式与在帖子中相同:用反引号括起代码(我认为反引号必须“接触”代码的非空格部分)。 - Andrey Tyukin
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接