2016-02-26 31 views
7

Kiedy próbuję skompilować następujący kod:„Błąd: Zamknięcie może przeżyć bieżącą funkcję”, ale nie przeżyje to

fn main() { 

    (...) 

    let mut should_end = false; 

    let mut input = Input::new(ctx); 

    input.add_handler(Box::new(|evt| { 
     match evt { 
      &Event::Quit{..} => { 
       should_end = true; 
      } 
      _ => {} 
     } 
    })); 

    while !should_end { 
     input.handle(); 
    } 
} 

pub struct Input { 
    handlers: Vec<Box<FnMut(i32)>>, 
} 

impl Input { 
    pub fn new() -> Self { 
     Input {handlers: Vec::new()} 
    } 
    pub fn handle(&mut self) { 
     for a in vec![21,0,3,12,1] { 
      for handler in &mut self.handlers { 
       handler(a); 
      } 
     } 
    } 
    pub fn add_handler(&mut self, handler: Box<FnMut(i32)>) { 
     self.handlers.push(handler); 
    } 
} 

otrzymuję ten błąd:

error: closure may outlive the current function, but it borrows `should_end`, which is owned by the current function 

mogę” t po prostu dodaj move do zamknięcia, ponieważ muszę później użyć should_end w pętli głównej. Mam na myśli, że mogę, ale ponieważ bool jest Copy, wpłynie to tylko na should_end wewnątrz zamknięcia, a tym samym pętle programu na zawsze.

O ile rozumiem, ponieważ input jest tworzony w głównej funkcji, a zamknięcie jest przechowywane w input, nie może być możliwe przeżycie aktualnej funkcji. Czy istnieje sposób, aby wyrazić Rustowi, że zamknięcie nie przetrwa main? A może jest taka możliwość, że nie widzę, aby zamknięcie przetrwało dłużej niż? W tym drugim przypadku, istnieje sposób na życie go tylko tak długo, jak main?

Czy muszę refaktoryzować sposób, w jaki przetwarzam dane wejściowe, czy jest jakiś sposób, w jaki mogę wykonać tę pracę. Jeśli potrzebuję refaktoryzacji, gdzie mogę zobaczyć dobry przykład tego w Rust? Jest to a playpen of a simplified version. Możliwe, że popełniłem błąd, który mógł spowodować awarię przeglądarki. Zdarzyło mi się kiedyś, więc uważaj.

Jeśli jest to potrzebne, resztę my code is available. Wszystkie istotne informacje powinny być albo main.rs lub input.rs.

Odpowiedz

8

Problem nie jest twoim zamknięciem, ale metodą add_handler. W pełni rozwinięty to będzie wyglądać następująco:

fn add_handler<'a>(&'a mut self, handler: Box<FnMut(i32) + 'static>) 

Jak widać, istnieje niejawna 'static związany na obiekcie cechy. Oczywiście my nie chcemy, więc wprowadzenie drugiego życia 'b:

fn add_handler<'a, 'b: 'a>(&'a mut self, handler: Box<FnMut(i32) + 'b>) 

Ponieważ jesteś dodanie obiektu handler pola Input::handlers, że pole nie może przeżyć zakres przedmiotu handler. Zatem musimy też ograniczyć jej żywotność:

pub struct Input<'a> { 
    handlers: Vec<Box<FnMut(i32) + 'a>>, 
} 

To znowu wymaga Impl mieć przez całe życie, które możemy wykorzystać w metodzie add_handler.

impl<'a> Input<'a> { 
    ... 
    pub fn add_handler(&mut self, handler: Box<FnMut(i32) + 'a>) { 
     self.handlers.push(handler); 
    } 
} 

Teraz wszystko, co pozostało stosuje Cell kontrolować dostęp do swojej flagi should_end.

+0

po prostu trzeba przeczytać na 'Cell' i takie. W przeciwnym razie, bardzo dokładna odpowiedź, dziękuję. – MrNosco

+0

Skąd pochodzi ta ukryta "statyczna", czy gdzieś to udokumentowano? – starblue

+0

iirc, jeśli nie określisz okresu istnienia, zawsze jest to ''static', z wyjątkiem argumentów funkcji i lokalnych powiązań, co oczywiście może sugerować krótsze okresy życia –

0

Oto an example of the fixed code:

use std::cell::Cell; 

fn main() { 
    let should_end = Cell::new(false); 
    let mut input = Input::new(); 
    input.add_handler(Box::new(|a| { 
     match a { 
      1 => { 
       should_end.set(true); 
      } 
      _ => { 
       println!("{} {}", a, should_end.get()) 
      } 
     } 
    })); 
    let mut fail_safe = 0; 
    while !should_end.get() { 
     if fail_safe > 20 {break;} 
     input.handle(); 
     fail_safe += 1; 
    } 
} 

pub struct Input<'a> { 
    handlers: Vec<Box<FnMut(i32) + 'a>>, 
} 

impl<'a> Input<'a> { 
    pub fn new() -> Self { 
     Input {handlers: Vec::new()} 
    } 
    pub fn handle(&mut self) { 
     for a in vec![21,0,3,12,1,2] {// it will print the 2, but it won't loop again 
      for handler in &mut self.handlers { 
       handler(a); 
      } 
     } 
    } 
    pub fn add_handler(&mut self, handler: Box<FnMut(i32) + 'a>) { 
     self.handlers.push(handler); 
    } 
}