2015-04-09 13 views
7

Próbuję stworzyć coś w stylu "systemu wywołań zwrotnych". Na przykład jest tam okno i kilka przycisków. Okno ustawia wywołania zwrotne dla każdego przycisku. Oba wywołania zwrotne powinny zmienić stan okna. Kompilator nie zezwala na przechwytywanie &self w moich zamknięciach/wywołaniach zwrotnych i nie wiem, jak to zrobić.Tworzenie systemu zwrotnego przy użyciu zamknięć

Czy istnieją jakieś typowe wzorce wywołań zwrotnych, które powinienem śledzić?

Jest to łatwy przykład, ponieważ wszystkie komponenty mają ten sam czas życia. Co się stanie, jeśli składniki mają różne okresy życia?

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

również istotne: http://stackoverflow.com/questions/32044301/cannot-pass-self-as-callback-parameter-due -do-podwójnego wypożyczania – Shepmaster

Odpowiedz

6

Idziesz do przedostania się do bezpośrednich problemów z tej linii:

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

Tutaj trzeba pożyczyć self jako zmienny w celu zmodyfikowania btn. Jesteś również próbuje pożyczyć self jako zmienne w zamknięciu. To spowoduje natychmiastowe problemy, ponieważ Rust nie zezwala na wielokrotne modyfikowanie odniesień do tego samego obiektu (znanego jako aliasing). Jest to podstawowa część gwarancji bezpieczeństwa pamięci dla tego języka.

Ponadto, koncepcyjnie próbujesz ustawić cykl odniesień - Window wie o Button i zna się na Window. Chociaż jest to możliwe, często nie jest to, czego chcesz. Gdy odniesienia mają cykl, bardzo trudno je rozdzielić. Możesz także wyszukiwać inne pytania, które pytają o tworzenie wykresów w Rust (w przeciwieństwie do drzew), aby zobaczyć podobne problemy, które mieli inni ludzie.

Idealnie można skonstruować swój kod jako drzewo. Tutaj wybrałem że Button może wiedzieć o Window, ale nie odwrotnie:

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

Czy to oznacza, że ​​nie mogę napisać przycisku, który zmienia swój tytuł po kliknięciu? Zobacz [kod] (http://is.gd/xW4sGp) – Marvel

+1

Właściwie mogę. Po kliknięciu zdarzenia do kolejki i obsługiwać je w pętli głównej, jak chcesz. [Pełny przykład] (http://is.gd/X39182) – Marvel

Powiązane problemy