在 Rust 中存储包含闭包的结构向量
Storing a Vector of structs containing closures in Rust
在我的 Rust 应用程序中,我想存储一个 Vector 结构,其中包含稍后调用的闭包。
到目前为止我有这样的东西(对于俄罗斯方块游戏):
pub struct TimestepTimer {
pub frameForExecution: i32,
pub func: Box<dyn Fn() -> ()>
}
pub struct Game {
pub frame: i32,
pub timers: Vec<TimestepTimer>
}
impl Game {
fn add_timer (&mut self, framesFromNow: i32, func: Box<dyn Fn() -> ()>) {
self.timers.push(TimestepTimer {
frameForExecution: self.frame + framesFromNow,
func
});
}
/* Example of a function that would use add_timer */
pub fn begin_gravity(&mut self) {
self.add_timer(30, Box::new(|| {
self.move_down();
// Gravity happens repeatedly
self.begin_gravity();
}));
}
// Later on would be a function that checks the frame no.
// and calls the timers' closures which are due
}
由于 E0495,目前这不起作用,表示 Box::new 创建的引用不能超过 begin_gravity 函数调用,而且 'must be valid for the static lifetime'
我对 Rust 很陌生,所以这可能根本不是惯用的解决方案。
我尝试的每个不同的解决方案似乎都遇到了不同的编译器错误(尽管我知道这是我的错,而不是编译器的错)
这基本上是 的副本:您的闭包保留对 self
的引用(以便它可以调用 self.move_down()
和 self.begin_gravity()
)并且您是试图将它存储在自己内部 → 那是不可能的。
如果您将闭包更改为采用 &mut Game
参数并对其进行操作,您可以获得类似的效果:
pub struct TimestepTimer {
pub frameForExecution: i32,
pub func: Box<dyn Fn(&mut Game) -> ()>
}
pub struct Game {
pub frame: i32,
pub timers: Vec<TimestepTimer>
}
impl Game {
fn add_timer (&mut self, framesFromNow: i32, func: Box<dyn Fn(&mut Game) -> ()>) {
self.timers.push(TimestepTimer {
frameForExecution: self.frame + framesFromNow,
func
});
}
/* Example of a function that would use add_timer */
pub fn begin_gravity(&mut self) {
self.add_timer(30, Box::new(|s: &mut Game| {
// self.move_down();
// Gravity happens repeatedly
s.begin_gravity();
}));
}
// Later on would be a function that checks the frame no.
// and calls the timers' closures which are due
}
但是现在你在调用定时器时会遇到麻烦,因为你需要引用 self.timers
来迭代它,同时你需要传递一个可变引用给 self
到闭包中,但编译器不允许您同时拥有对 self.timers
的不可变引用和对 self
的可变引用。如果你真的想把你的计时器保持在 Vec
中,解决这个问题的最简单方法是用一个空向量换出 timers
并在你去的时候重新填充:
pub fn step (&mut self) {
self.frame += 1;
let mut timers = vec![];
std::mem::swap (&mut self.timers, &mut timers);
for t in timers {
if self.frame > t.frameForExecution {
(t.func)(self);
} else {
self.timers.push (t);
}
}
}
然而,将计时器存储在 BinaryHeap
中可能会更好,这样可以实现更清晰、更高效的执行循环:
pub fn step (&mut self) {
self.frame += 1;
while let Some (t) = self.timers.peek() {
if t.frameForExecution >= self.frame { break; }
// `unwrap` is ok here because we know from the `peek` that
// there is at least one timer in `timers`
let t = self.timers.pop().unwrap();
(t.func)(self);
}
}
这将需要在 TimestepTimer
上实施 Ord
以及一些其他特征:
impl Ord for TimestepTimer {
fn cmp (&self, other: &Self) -> Ordering {
// Note the swapped order for `self` and `other` so that the `BinaryHeap`
// will return the earlier timers first.
other.frameForExecution.cmp (&self.frameForExecution)
}
}
impl PartialOrd for TimestepTimer {
fn partial_cmp (&self, other: &Self) -> Option<Ordering> {
Some (self.cmp (other))
}
}
impl PartialEq for TimestepTimer {
fn eq (&self, other: &Self) -> bool {
self.frameForExecution == other.frameForExecution
}
}
impl Eq for TimestepTimer {}
在我的 Rust 应用程序中,我想存储一个 Vector 结构,其中包含稍后调用的闭包。
到目前为止我有这样的东西(对于俄罗斯方块游戏):
pub struct TimestepTimer {
pub frameForExecution: i32,
pub func: Box<dyn Fn() -> ()>
}
pub struct Game {
pub frame: i32,
pub timers: Vec<TimestepTimer>
}
impl Game {
fn add_timer (&mut self, framesFromNow: i32, func: Box<dyn Fn() -> ()>) {
self.timers.push(TimestepTimer {
frameForExecution: self.frame + framesFromNow,
func
});
}
/* Example of a function that would use add_timer */
pub fn begin_gravity(&mut self) {
self.add_timer(30, Box::new(|| {
self.move_down();
// Gravity happens repeatedly
self.begin_gravity();
}));
}
// Later on would be a function that checks the frame no.
// and calls the timers' closures which are due
}
由于 E0495,目前这不起作用,表示 Box::new 创建的引用不能超过 begin_gravity 函数调用,而且 'must be valid for the static lifetime'
我对 Rust 很陌生,所以这可能根本不是惯用的解决方案。
我尝试的每个不同的解决方案似乎都遇到了不同的编译器错误(尽管我知道这是我的错,而不是编译器的错)
这基本上是 self
的引用(以便它可以调用 self.move_down()
和 self.begin_gravity()
)并且您是试图将它存储在自己内部 → 那是不可能的。
如果您将闭包更改为采用 &mut Game
参数并对其进行操作,您可以获得类似的效果:
pub struct TimestepTimer {
pub frameForExecution: i32,
pub func: Box<dyn Fn(&mut Game) -> ()>
}
pub struct Game {
pub frame: i32,
pub timers: Vec<TimestepTimer>
}
impl Game {
fn add_timer (&mut self, framesFromNow: i32, func: Box<dyn Fn(&mut Game) -> ()>) {
self.timers.push(TimestepTimer {
frameForExecution: self.frame + framesFromNow,
func
});
}
/* Example of a function that would use add_timer */
pub fn begin_gravity(&mut self) {
self.add_timer(30, Box::new(|s: &mut Game| {
// self.move_down();
// Gravity happens repeatedly
s.begin_gravity();
}));
}
// Later on would be a function that checks the frame no.
// and calls the timers' closures which are due
}
但是现在你在调用定时器时会遇到麻烦,因为你需要引用 self.timers
来迭代它,同时你需要传递一个可变引用给 self
到闭包中,但编译器不允许您同时拥有对 self.timers
的不可变引用和对 self
的可变引用。如果你真的想把你的计时器保持在 Vec
中,解决这个问题的最简单方法是用一个空向量换出 timers
并在你去的时候重新填充:
pub fn step (&mut self) {
self.frame += 1;
let mut timers = vec![];
std::mem::swap (&mut self.timers, &mut timers);
for t in timers {
if self.frame > t.frameForExecution {
(t.func)(self);
} else {
self.timers.push (t);
}
}
}
然而,将计时器存储在 BinaryHeap
中可能会更好,这样可以实现更清晰、更高效的执行循环:
pub fn step (&mut self) {
self.frame += 1;
while let Some (t) = self.timers.peek() {
if t.frameForExecution >= self.frame { break; }
// `unwrap` is ok here because we know from the `peek` that
// there is at least one timer in `timers`
let t = self.timers.pop().unwrap();
(t.func)(self);
}
}
这将需要在 TimestepTimer
上实施 Ord
以及一些其他特征:
impl Ord for TimestepTimer {
fn cmp (&self, other: &Self) -> Ordering {
// Note the swapped order for `self` and `other` so that the `BinaryHeap`
// will return the earlier timers first.
other.frameForExecution.cmp (&self.frameForExecution)
}
}
impl PartialOrd for TimestepTimer {
fn partial_cmp (&self, other: &Self) -> Option<Ordering> {
Some (self.cmp (other))
}
}
impl PartialEq for TimestepTimer {
fn eq (&self, other: &Self) -> bool {
self.frameForExecution == other.frameForExecution
}
}
impl Eq for TimestepTimer {}