如何拥有物品可以访问容器的容器?

时间:2015-10-05 12:47:40

标签: rust

我正在尝试实现一个容器,其中包含GUI小部件列表,其中每个小部件都需要访问该容器。每个小部件可能需要在某些事件上修改其他小部件。例如,当用户单击按钮时,编辑器文本将更新。

我可以使用盒装use std::collections::HashMap; struct SharedItem<'a> { pub value: String, pub store: &'a HashMap<String, SharedItem<'a>>, } fn trigger_button(button: &SharedItem) { // use case where SharedItem has to be mutable let mut editor = button.store.get(&"editor".to_string()).unwrap(); editor.value = "value inserted by button".to_string(); } fn main() { // map shared items by their name let mut shared_store: HashMap<String, SharedItem> = HashMap::new(); // create components let editor = SharedItem { value: "editable content".to_string(), store: &shared_store, }; let button = SharedItem { value: "button".to_string(), store: &shared_store, }; shared_store.insert("button".to_string(), button); shared_store.insert("editor".to_string(), editor); // now update the editor by triggering button trigger_button(shared_store.get(&"button".to_string()).unwrap()); } ,但它无法解决问题。什么是实现我需要的最简单方法?

这是我目前所拥有的,它没有编译,但你会得到这个想法:

{{1}}

2 个答案:

答案 0 :(得分:3)

  

这是必需的,因为小部件可能需要在某些事件上修改其他小部件。例如:用户单击按钮,结果编辑器文本将更新。

正因为这就是大多数GUI在OO语言中的运作方式并不意味着必须这样做。

在这种特定情况下,一个简单的解决方案是:

  1. 将小部件ID存储为编辑框的ID。
  2. 将事件发布到某个事件队列中,指示要执行的更新以及要执行它的窗口小部件(按ID)。
  3. 因此,在任何时间点,系统中最多存在一个小部件上的一个可变句柄,这样可以避免别名并使Rust保持高兴。

    注意:这个答案假定您不需要编辑器小部件的同步响应,如果您使用这样的系统,则会陷入回调地狱。

答案 1 :(得分:3)

  

如何拥有物品可以访问容器的容器?

cannot do this with references。您可以使用reference-counted boxed values执行此操作。

但是,你仍然可以通过不同的方式思考来解决问题...

  

每个小部件都需要访问该容器。每个小部件可能需要修改某些事件上的其他小部件

如果它适合您的问题域,只需在单向数据流中发布事件就更清晰了:

// Create meaningful events with data pertinent to those events.
enum Event {
    UpdateText,
    Click,
}

struct Events(Vec<Event>);

impl Events {
    fn push(&mut self, event: Event) { self.0.push(event) }
}

trait Widget {
    fn event_happened(&mut self, event: &Event, triggered_events: &mut Events);
    fn draw(&self);
}

struct Widgets(Vec<Box<Widget>>);

struct TextField(String);

impl Widget for TextField {
    fn event_happened(&mut self, event: &Event, _: &mut Events) {
        match *event {
            Event::UpdateText => self.0.push_str("event"),
            _ => (),
        }
    }

    fn draw(&self) {
        println!("Drawing text: {}", self.0);
    }
}

struct Button;

impl Widget for Button {
    fn event_happened(&mut self, event: &Event, triggered_events: &mut Events) {
        match *event {
            Event::Click => triggered_events.push(Event::UpdateText),
            _ => (),
        }
    }

    fn draw(&self) {
        println!("Drawing button");
    }
}

fn main() {
    let mut events = Events(vec![]);

    let mut widgets = Widgets(vec![
        Box::new(TextField(String::new())),
        Box::new(Button),
    ]);

    // This would probably loop forever until a shutdown event was posted
    for i in 0..10 {
        for widget in &widgets.0 {
            widget.draw();
        }

        // Fake a click at some point
        if i == 0 {
            events.push(Event::Click);
        }

        let mut next_events = Events(vec![]);
        for event in &events.0 {
            for widget in &mut widgets.0 {
                widget.event_happened(event, &mut next_events);
            }
        }
        events = next_events;
    }
}

要清楚,这是Matthieu M.建议的内容(没有目标ID);我刚刚输入了这个例子,所以我想发布它^ _ ^。