【问题标题】:How to I deal with mutually dependent Rust borrows?如何处理相互依赖的 Rust 借用?
【发布时间】:2018-03-23 01:08:18
【问题描述】:

我正在用 Rust 写一个 Rogue-like。我有一个Player,其中有一个Vec 的盒装TimedEffects。定时效果具有activate(&mut Player)deactivate(&mut Player) 方法,以及减少剩余时间的tick() 方法。当它变为零时,应该调用deactivate() 方法。

pub struct Player {
    timed_effects: Vec<Box<TimedEffect>>,
}

每次玩家移动时,我都需要减少剩余时间。这是在 Player 方法中完成的:

impl Player {
    fn regenerate(&mut self) {
        self.timed_effects.iter_mut().for_each(|te| te.tick());

        for i in 0..self.timed_effects.len() {
            if self.timed_effects[i].ticks_remaining() == 0 {
                let bte = self.timed_effects.swap_remove(i);
                bte.deactivate(self);
            }
        }
    }
}

我最初尝试将self 参数(&amp;mut Player) 传递给TimedEffect 对象的tick() 方法,当时间归零时自动调用deactivate(player) 的逻辑。

对向量的迭代构成对 (Player) self 的借用,这与将 &amp;mut Player 参数传递给 tick() 甚至与 .filter 进行单独迭代不兼容。

相反,如图所示,我发现自己在一个整数范围内进行迭代,将框从向量中提取到一个局部变量中,然后对其调用 .deactivate(self)

这很痛苦。

鉴于效果需要与其受害者建立某种联系,以便deactivate() 方法可以撤消效果,我应该如何构建这些对象,以免我陷入这个依赖借用网络中?

【问题讨论】:

  • 请提供minimal reproducible example。我的猜测是,您在某处遇到了一些编译器错误,可以将您指向合适的现有问答,但我们看不到错误是什么。缺少 MCVE 还意味着我们必须重新键入所有依赖代码,这可能会引入令人兴奋的新错误。
  • 话虽如此,使用我的推理能力,我猜它是How can I call a mutating method while holding a reference to self?Passing mutable self reference to method of owned objectcannot borrow self.x as immutable because *self is also borrowed as mutable 中的一个或多个的副本。请继续edit您的问题,以解释为什么这些问题没有回答或将问题标记为重复。
  • 我正在学习 Rust - 我到处都是编译器错误!但是我遵循了这些错误,并生成了显示的代码,通过不使用 rust 的迭代器来避免借用。我的问题实际上是关于设计的,哪些设计更改可以产生更好的结果。 (我没有错误的事实可能阻碍了我的搜索,因为我没有正确的关键字......)
  • Rust 正在保护你未来的自己免于在脚下开枪。想象一下迭代.tick(&amp;mut player) 现在可以工作了。如果将来您决定添加一个定时效果,在.deactivate() 上移除播放器的所有其他定时效果怎么办?你最终会得到一个无效的迭代器。强迫你采用的设计锈没有那个问题。

标签: rust


【解决方案1】:

为避免借款问题,您可以分两步进行:

  1. 将完成的效果从timed_effects 转移到缓冲区,
  2. 遍历缓冲区并调用deactivate

我将使用 nightly 的 Vec::drain_filter 来获取更简洁的代码,尽管这当然不是强制性的。

fn regenerate(&mut self) {
    self.timed_effects.iter_mut().for_each(|te| te.tick());

    let timed_out: Vec<_> =
        self.timed_effects
            .drain_filter(|t| t.ticks_remaining() == 0)
            .collect();

    //  Split iteration in a second step to stop borrowing self.
    for timer in timed_out {
        timer.deactivate(self);
    }
}

为了提高效率,您可以将可重用缓冲区传递给regenerate(以避免分配)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 2014-02-18
    • 1970-01-01
    • 1970-01-01
    • 2018-10-19
    • 2015-12-13
    相关资源
    最近更新 更多