提问人:Bernard 提问时间:1/29/2023 更新时间:1/29/2023 访问量:92
在 Rust 中,同时在多个子对象上安装可变回调函数的惯用方法是什么?
In Rust, what is the idiomatic way to install a mutable callback function on multiple subobjects at the same time?
问:
我有一个算法,可以以某种方式操作对象数组,但调用者需要能够侦听由该算法触发的某些事件(对象的更新)。
以下是我正在尝试执行的简化示例。 (Rust playground)
这是算法模块:
// Some module containing the algorithm.
// This module doesn't want to know what the caller wants to do in the listener.
trait Listener {
fn update(&mut self, s: String);
}
struct Object<L: Listener> {
/* other stuff */
listener: L,
}
fn do_stuff<L: Listener>(objects: &mut [Object<L>]) {
// do stuff, which eventually might call the listener of each object any number of times.
objects[0].listener.update("zero".to_string());
objects[1].listener.update("one".to_string());
objects[0].listener.update("zeroes".to_string());
}
调用方将调用 ,它以某种方式改变对象数组,并在改变每个对象时调用侦听器。此算法模块不需要知道调用方在触发回调时想要做什么。do_stuff()
这是主要模块:
// Main module
// Obviously this can't implement Copy and Clone, because of the mut ref.
struct MyListener<'a>{
node: &'a mut Vec<String>,
prefix: &'static str,
}
impl<'a> Listener for MyListener<'a> {
fn update(&mut self, s: String) {
self.node.push(format!("{} {}", self.prefix, s));
}
}
pub fn main() {
let mut strings = Vec::new();
let mut objects = vec![
Object{listener: MyListener{node: &mut strings, prefix: "red"}},
Object{listener: MyListener{node: &mut strings, prefix: "blue"}},
Object{listener: MyListener{node: &mut strings, prefix: "green"}},
];
do_stuff(&mut objects);
}
错误:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `strings` as mutable more than once at a time
--> src/main.rs:37:43
|
35 | let mut objects = vec![
| _______________________-
36 | | Object{listener: MyListener{node: &mut strings, prefix: "red"}},
| | ------------ first mutable borrow occurs here
37 | | Object{listener: MyListener{node: &mut strings, prefix: "blue"}},
| | ^^^^^^^^^^^^ second mutable borrow occurs here
38 | | Object{listener: MyListener{node: &mut strings, prefix: "green"}},
39 | | ];
| |_____- first borrow later used here
error[E0499]: cannot borrow `strings` as mutable more than once at a time
--> src/main.rs:38:43
|
35 | let mut objects = vec![
| _______________________-
36 | | Object{listener: MyListener{node: &mut strings, prefix: "red"}},
| | ------------ first mutable borrow occurs here
37 | | Object{listener: MyListener{node: &mut strings, prefix: "blue"}},
38 | | Object{listener: MyListener{node: &mut strings, prefix: "green"}},
| | ^^^^^^^^^^^^ second mutable borrow occurs here
39 | | ];
| |_____- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to 2 previous errors
在我的实际代码中,我没有更新 Vec,而是尝试调用一个可变函数(从 svg 板条箱)向文档添加一些 SVG 节点。该算法将对象转换为原始形状,每个侦听器获取这些原始形状和当前对象的样式(例如线宽、颜色),并生成 SVG 命令并将它们添加到 .&mut svg::Document
svg::Document
上面的代码显然是行不通的,因为我多次可变地借用向量。但是实际上没有任何方法可以将其拆分,它们都需要修改相同的向量。strings
我也可以do_stuff返回更新列表并让调用方稍后应用它们,但这将涉及一堆临时向量。
有什么方法可以使当前的设计起作用吗?
答:
2赞
Chayim Friedman
1/29/2023
#1
将向量包装在 RefCell
中,然后使用共享引用:strings
use std::cell::RefCell;
struct MyListener<'a> {
node: &'a RefCell<Vec<String>>,
prefix: &'static str,
}
impl<'a> Listener for MyListener<'a> {
fn update(&mut self, s: String) {
self.node.borrow_mut().push(format!("{} {}", self.prefix, s));
}
}
pub fn main() {
let strings = RefCell::new(Vec::new());
let mut objects = vec![
Object { listener: MyListener { node: &strings, prefix: "red" }},
Object { listener: MyListener { node: &strings, prefix: "blue" }},
Object { listener: MyListener { node: &strings, prefix: "green" }},
];
do_stuff(&mut objects);
}
评论
0赞
Bernard
2/1/2023
这可行,是的,但我不禁觉得有点不幸,因为这个问题没有更好的抽象。
0赞
Chayim Friedman
2/2/2023
@Bernard 你说的“更好的抽象”是什么意思?在这里可以构建一个零成本的抽象,因为你只需要通过共享引用来推送,但应该足够好(更好的抽象的例子)。它没有在该标准库中实现,因为与 不同,它不是通用的,因此不是通常需要的。RefCell
RefCell
评论
&mut Vec<String>
Vec<String>
Rc<RefCell<Vec<String>>>
&mut Vec<String>
Rc
&'a RefCell<Vec<String>>
RefCell