【发布时间】:2021-10-10 20:44:26
【问题描述】:
希望实现两种风格的回调接口。不可变(不能改变分配回调的结构)和可变(可以改变分配回调的结构)。
type Callback<'a> = dyn FnMut(&'a MyStruct<'a>);
type CallbackMut<'a> = dyn FnMut(&'a mut MyStruct<'a>);
struct MyStruct<'a> {
callback: &'a Callback<'a>,
callback_mut: &'a CallbackMut<'a>
}
impl<'a> MyStruct<'a> {
pub fn new(callback: &'a Callback<'a>, callback_mut: &'a CallbackMut<'a>) -> MyStruct<'a> {
MyStruct {
callback,
callback_mut,
}
}
pub fn trigger_callback(&'a self) {
(self.callback)(self);
}
pub fn trigger_callback_mut(&'a mut self) {
(self.callback_mut)(self);
}
}
#[cfg(test)]
mod tests {
use crate::minimal::*;
#[test]
fn it_works() {
let mut triggered1 = false;
let callback = |_my_struct: &MyStruct| {
triggered1 = true;
};
let mut triggered2 = false;
let callback_mut = |_my_struct: &mut MyStruct| {
triggered2 = true;
};
let mut my_struct = MyStruct::new(&callback, &callback_mut);
my_struct.trigger_callback();
my_struct.trigger_callback_mut();
assert!(triggered1, "Should call immutable callback");
assert!(triggered2, "Should call mutable callback");
}
}
所以我试图了解如何使这种模式在 Rust 中工作,并且对如何解决以下几个编译器错误感到困惑。
- 如何使用分配的回调并将结构传递给它?还有哪些其他更适合 Rust 的模式?
error[E0596]: cannot borrow `*self.callback_mut` as mutable, as it is behind a `&` reference
--> src/minimal.rs:22:9
|
6 | callback_mut: &'a CallbackMut<'a>
| ------------------- help: consider changing this to be mutable: `&'a mut CallbackMut<'a>`
...
22 | (self.callback_mut)(self);
| ^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
- 如何改变回调中捕获的变量?还有哪些其他更适合 Rust 的模式?
error[E0597]: `triggered1` does not live long enough
--> src/minimal.rs:35:13
|
34 | let callback = |_my_struct: &MyStruct| {
| ----------------------- value captured here
35 | triggered1 = true;
| ^^^^^^^^^^ borrowed value does not live long enough
...
43 | let mut my_struct = MyStruct::new(&callback, &callback_mut);
| --------- cast requires that `triggered1` is borrowed for `'static`
...
48 | }
| - `triggered1` dropped here while still borrowed
- 当不可变借用直到可变借用才存在时,我似乎应该能够执行不可变借用操作,然后严格执行可变借用操作。
error[E0502]: cannot borrow `my_struct` as mutable because it is also borrowed as immutable
--> src/minimal.rs:45:9
|
44 | my_struct.trigger_callback();
| --------- immutable borrow occurs here
45 | my_struct.trigger_callback_mut();
| ^^^^^^^^^^--------------------^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
- 然后我将如何引用在回调中增加的变量?
error[E0503]: cannot use `triggered1` because it was mutably borrowed
--> src/minimal.rs:46:17
|
34 | let callback = |_my_struct: &MyStruct| {
| ----------------------- borrow of `triggered1` occurs here
35 | triggered1 = true;
| ---------- borrow occurs due to use of `triggered1` in closure
...
43 | let mut my_struct = MyStruct::new(&callback, &callback_mut);
| --------- cast requires that `triggered1` is borrowed for `'static`
...
46 | assert!(triggered1, "Should call immutable callback");
| ^^^^^^^^^^ use of borrowed `triggered1`
编辑:一些附加信息。
希望在状态机中实现挂钩功能,从而我可以在事件上调用挂钩 - 状态进入、状态退出、边缘遍历。
不可变回调示例
let mut collected_values = Vec::new();
let immutable_callback_example = |my_struct: &MyStruct| {
collected_values.push(my_struct.some_val.clone());
};
# run through a bunch of code that might call the trigger_callback an arbitrary number of times
# Do something with the collected values - report out etc
let mut my_structs = Vec::new();
let immutable_callback_example = |my_struct: &MyStruct| {
# Some event indicates we need to create a new struct
state_machines.push(MyStruct {
-10,
callback,
callback_mut,
});
};
# run through a bunch of code that might call the trigger_callback an arbitrary number of times
# Do something with the final collection of structs - report out etc
可变回调示例
let mut collected_values = Vec::new();
let mutable_callback_example = |my_struct: &mut MyStruct| {
collected_values.push(my_struct.some_val.clone());
# Wrap this back around to 0
if my_struct.some_val > 10 {
my_struct.some_val = 0;
}
};
# run through a bunch of code that might call the trigger_callback_mut an arbitrary number of times
# Again, report out
【问题讨论】:
-
您的回调真的需要访问
MyStruct值吗?您的两个测试都没有使用它们,因此很难就如何解决其中的一些问题提出建议。另外,为什么要存储对函数的引用而不是函数本身? -
这个设计根本行不通。如果这些回调需要是
FnMut,这意味着它们可以修改它们的捕获变量,这意味着它们正在改变自己。这意味着,这些字段必须是callback: &'a mut Callback<'a>。但是你永远不能在传递&self时调用它们,因为你将同时拥有一个不可变和可变借用。如果允许,这些函数实际上可能会做坏事。 -
很难就如何解决您的问题提出建议,因为我们不知道您实际需要实现什么。也许可以通过渠道解决?我不知道。
-
这样的事情怎么样:play.rust-lang.org/…
-
这个怎么样:play.rust-lang.org/…
标签: design-patterns rust callback borrow-checker