2015-04-09 11 views
7

Ich versuche, so etwas wie ein "Rückruf-System" zu machen. Zum Beispiel gibt es ein Fenster und ein paar Knöpfe darin. Das Fenster legt Rückrufe für jede Schaltfläche fest. Beide Rückrufe sollten den Status des Fensters ändern. Der Compiler erlaubt nicht, &self in meinen Schließungen/Rückrufe zu erfassen, und ich weiß nicht, wie es funktioniert.Erstellen eines Rückrufsystems mit Schließungen

Gibt es allgemeine Muster für Rückrufe, denen ich folgen sollte?

Dies ist ein einfaches Beispiel, da alle Komponenten die gleiche Lebensdauer haben. Was ist, wenn die Komponenten unterschiedliche Lebensdauern haben?

struct Button<'a> { 
    f: Option<Box<Fn() + 'a>>, 
} 

impl<'a> Button<'a> { 
    fn new() -> Button<'a> { Button { f: None } } 
    fn set<T: Fn() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); } 
    fn unset(&mut self) { self.f = None; } 
    fn call(&self) { match self.f { Some(ref f) => f(), None =>() } } 
} 

struct Window<'a> { 
    btn: Button<'a>, 
    //btns: Vec<Button<'a>>, 
} 

impl<'a> Window<'a> { 
    fn new() -> Window<'a> { 
     Window { btn: Button::new() } 
    } 

    fn hi(&mut self) { // self is mutable 
     println!("callback"); 
    } 

    fn run(&mut self) { 
     // Compile error: cannot infer an appropriate lifetime for 
     // capture of `self` by closure due to conflicting requirements 
     self.btn.set(|| self.hi()); // How to make it work? 
     self.btn.call(); 
     self.btn.unset(); 
    } 
} 

fn main() { 
    let mut wnd = Window::new(); 
    wnd.run(); 
} 
+0

Ebenfalls relevant: http://stackoverflow.com/questions/32044301/cannot-pass-self-as-callback-parameter-due -zu-doppelt-leihen – Shepmaster

Antwort

6

Sie werden in unmittelbare Probleme mit dieser Linie laufen:

self.btn.set(|| self.hi()); 

Hier müssen Sie self wie wandelbar, um borgen btn zu ändern. Sie sind auch versuchen, self als veränderlich in der Schließung ausleihen. Dies wird sofort zu Problemen führen, da Rust Ihnen nicht erlaubt, mehrere veränderbare Referenzen auf das gleiche Objekt zu haben (bekannt als Aliasing). Dies ist ein grundlegender Teil der Speichergarantien der Sprache.

Auch konzeptionell versuchen Sie, einen Zyklus von Referenzen einzurichten - die Window kennt Button und Button weiß über Window. Während dies möglich ist, ist es oft nicht das, was Sie wollen. Sobald die Referenzen einen Zyklus haben, ist es sehr schwierig, sie zu entwirren. Sie können auch andere Fragen, die Fragen über die Erstellung Graphen in Rust (im Gegensatz zu Bäume) fragen, um ähnliche Probleme zu sehen, die andere Leute gehabt haben.

Im Idealfall können Sie Ihren Code als Baum strukturieren. Hier wählte ich, dass Button kann über Window wissen, aber nicht umgekehrt:

struct Button<'a> { 
    f: Option<Box<FnMut() + 'a>>, 
} 

impl<'a> Button<'a> { 
    fn new() -> Button<'a> { Button { f: None } } 
    fn set<T: FnMut() + 'a>(&mut self, f: T) { self.f = Some(Box::new(f)); } 
    fn unset(&mut self) { self.f = None; } 
    fn call(&mut self) { match self.f { Some(ref mut f) => f(), None =>() } } 
} 

struct Window; 

impl Window { 
    fn hi(&mut self) { 
     println!("callback"); 
    } 
} 

fn main() { 
    let mut wnd = Window; 
    let mut btn = Button::new(); 
    btn.set(|| wnd.hi()); 
    btn.call(); 
    btn.unset(); 
} 
+0

Bedeutet das, dass ich keinen Knopf schreiben kann, der seinen eigenen Titel beim Klicken ändert? Siehe [der Code] (http://is.gd/xW4sGp) – Marvel

+1

Eigentlich kann ich. Poste Klickereignisse in eine Warteschlange und handle sie in der Hauptschleife wie du willst. [Das vollständige Beispiel] (http://is.gd/X39182) – Marvel