2015-10-05 51 views
2

我想實現一個容器,其中包含一個GUI小部件列表,每個小部件需要訪問該容器。每個小部件可能需要修改某些事件上的其他小部件。例如,當用戶點擊按鈕時,編輯器文本將被更新。我怎樣纔能有一個容器的物品可以訪問容器?

我可以使用盒裝的HashMap但它不能解決問題。什麼是最簡單的方法來實現我所需要的?

這是我現在有,它不會編譯,但你會得到的想法:

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

這將是[此問題]的副本(http://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that- value-in-the-the-struct) – Shepmaster

+0

@Shepmaster是的,它是同一個問題的變體,但它沒有解決方案,我想知道如何解決它。主要要求是從存儲器內的物品訪問存儲結構。 – insanebits

+1

http://stackoverflow.com/questions/27001067/how-can-i-make-a-structure-with-internal-references/27011347#27011347實際上更接近。特別是因爲它有這個問題的答案:使用引用計數框 –

回答

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);我剛剛已經輸入了這個例子,所以我想把它發佈^ _ ^。

+0

這將是方式比防止鐵鏽記憶安全更好的方法。從面向對象的背景轉變你的思維並不是那麼容易..我會接受它作爲答案。謝謝你的努力! – insanebits

+1

*反對防鏽記憶安全* - 要清楚,沒有什麼**不安全**關於使用'Rc >',它只是推遲執行從編譯時到運行時的內存安全。 – Shepmaster

+0

@Shepmaster:另一方面,儘管它很安全,但很容易導致某個參考週期結束並導致內存泄漏,這很煩人。 –

3

這是必需的,因爲部件可能需要修改某些事件的其他部件。例如:用戶單擊按鈕,因此編輯器文本將得到更新。

正因爲這是大多數GUI在OO語言中的操作方式,所以並不意味着這是必須完成的。

在這種特定情況下,一個簡單的解決方案是:

  1. 存儲爲微件ID的編輯框的ID。
  2. 將事件發佈到某個事件隊列中,指示要執行的更新以及要執行的更新(通過ID)。

因此,在任何時間點,系統中最多隻有一個可變控制柄存在於系統中,這樣可以避免混淆並保持Rust的快樂。

注意:此答案假定您不需要編輯器小部件的同步響應,如果您使用此類系統並且使用此類系統,則會陷入回調地獄。

相關問題