gpt4 book ai didi

rust - 引用字段调用回调

转载 作者:行者123 更新时间:2023-11-29 08:23:42 25 4
gpt4 key购买 nike

考虑这样的代码:

trait OnUpdate {
fn on_update(&mut self, x: &i32);
}

struct Foo {
field: i32,
cbs: Vec<Box<OnUpdate>>,
}

impl Foo {
fn subscribe(&mut self, cb: Box<OnUpdate>) {
self.cbs.push(cb);
}
fn set_x(&mut self, v: i32) {
self.field = v;

//variant 1
//self.call_callbacks(|v| v.on_update(&self.field));

//variant 2
let f_ref = &self.field;
for item in &mut self.cbs {
item.on_update(f_ref);
}
}
fn call_callbacks<CB: FnMut(&mut Box<OnUpdate>)>(&mut self, mut cb: CB) {
for item in &mut self.cbs {
cb(item);
}
}
}

如果我注释变体 2 并取消注释变体 1,它无法编译,因为我同时需要 &Foo&mut Foo

但我真的需要这个地方的功能,因为我需要同样的功能在几个地方调用回调的代码。

那么我这里是否需要宏来调用回调,或者可能是另一种解决方案?

旁注:在实际代码中,我使用大结构而不是 i32,所以我不能复制它。我在 OnUpdate 中也有几个方法,所以我需要 call_callbacks 中的 FnMut

最佳答案

Rust 的借用检查器的一个重要规则是,可变访问是独占访问。

在变体 2 中,这条规则得到支持,因为对 self.fieldmut self.cbs 的引用从未真正重叠。 for 循环在 &mut Vec 上隐式调用 into_iter,它返回一个 std::slice::IterMut 对象引用 vector,但不引用 Foo 的其余部分。换句话说,for 循环并不真正包含对 self 的可变借用。

在变体 1 中,有一个 call_callbacks 确实保留了一个可变的 self 借用,这意味着它不能接收(直接或间接)另一个 self 借用。换句话说,同时:

  1. 它接受对 self 的可变引用,这允许它修改它的所有字段,包括 self.field

  2. 它接受一个引用self的闭包,因为它使用表达式self.field

让这个编译将允许 call_callbacks 改变 self.field 而闭包不会意识到它。在整数的情况下,这听起来可能没什么大不了的,但对于其他数据,这将导致错误,而 Rust 的借用检查器是明确设计来防止的。例如,Rust 依靠这些属性来防止在多线程程序中对可变容器或数据竞争进行不安全迭代。

在您的情况下,避免上述情况很简单。 set_x 控制着闭包的内容和对 self.field 的突变。可以重申将临时变量传递给闭包,然后更新 self.field,如下所示:

impl Foo {
fn subscribe(&mut self, cb: Box<OnUpdate>) {
self.cbs.push(cb);
}
fn set_x(&mut self, v: i32) {
self.call_callbacks(|cb| cb.on_update(&v));
self.field = v;
}

fn call_callbacks<OP>(&mut self, mut operation: OP)
where OP: FnMut(&mut OnUpdate)
{
for cb in self.cbs.iter_mut() {
operation(&mut **cb);
}
}
}

Rust 对这段代码没有问题,效果是一样的。

作为练习,可以编写一个像变体 2 一样工作的 call_callbacks 版本。在这种情况下,它需要接受一个迭代器到 cbs Vec ,就像 for 循环一样,它根本不能接受 &self:

fn set_x(&mut self, v: i32) {
self.field = v;
let fref = &self.field;
Foo::call_callbacks(&mut self.cbs.iter_mut(),
|cb| cb.on_update(fref));
}

fn call_callbacks<OP>(it: &mut Iterator<Item=&mut Box<OnUpdate>>,
mut operation: OP)
where OP: FnMut(&mut OnUpdate)
{
for cb in it {
operation(&mut **cb);
}
}

关于rust - 引用字段调用回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46135457/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com