简体   繁体   English

在 Rust 中,同时在多个子对象上安装可变回调 function 的惯用方法是什么?

[英]In Rust, what is the idiomatic way to install a mutable callback function on multiple subobjects at the same time?

I have an algorithm that manipulates an array of objects in some manner, but the caller needs to be able to listen to certain events (updates on the objects) triggered by the algorithm.我有一个以某种方式操纵对象数组的算法,但调用者需要能够侦听由该算法触发的某些事件(对象的更新)。

The following is a simplified example of what I'm trying to do.以下是我正在尝试做的一个简化示例。 ( Rust playground ) ( Rust游乐场)

This is the algorithm module:这是算法模块:

// 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());
}

Callers will call do_stuff() , which mutates an array of objects in some manner, and invokes the listener on each object as it mutates them.调用者将调用do_stuff() ,它以某种方式改变对象数组,并在每个 object 上调用监听器,因为它改变了它们。 This algorithm module should not need to know what the caller wants to do when the callback is triggered.当回调被触发时,这个算法模块应该不需要知道调用者想要做什么。

This is the main module:这是主要模块:

// 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);
}

Error:错误:

   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

In my actual code, instead of updating the Vec, I'm trying to call a mutable function on &mut svg::Document (from the svg crate) to add a some SVG nodes to the document.在我的实际代码中,我没有更新 Vec,而是尝试在&mut svg::Document (来自svg板条箱)上调用可变 function 以向文档添加一些 SVG 节点。 The algorithm converts the objects into primitive shapes, and each listener takes those primitive shapes and the style of the current object (eg line width, color), and generates SVG commands and adds them to the svg::Document .该算法将对象转换为原始形状,每个侦听器采用这些原始形状和当前 object 的样式(例如线宽、颜色),并生成 SVG 条命令并将它们添加到svg::Document中。

The code above obviously isn't going to work, because I'm borrowing the strings vector mutably multiple times.上面的代码显然是行不通的,因为我多次可变地借用了strings向量。 But there isn't really any way to split it up, all of them need to modify the same vector.但是实际上没有任何方法可以将其拆分,所有这些都需要修改同一个向量。

I could alternatively have do_stuff return the list of updates and have the caller apply them later, but that would involve a bunch of temporary vectors.我也可以让 do_stuff 返回更新列表并让调用者稍后应用它们,但这会涉及一堆临时向量。

Is there any way to make the current design work?有什么方法可以使当前的设计工作吗?

Wrap the strings vector in a RefCell , then use shared references:strings向量包装在RefCell中,然后使用共享引用:

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM