2016-02-26 13 views
7

Quando provo a compilare il codice seguente:"errore: chiusura può sopravvivere la funzione corrente", ma non sopravviverà lo

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); 
    } 
} 

ottengo questo errore:

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

posso' t semplicemente aggiungere move alla chiusura, perché ho bisogno di utilizzare should_end più avanti nel ciclo principale. Voglio dire, posso, ma dal momento che bool è Copy, interesserà solo lo should_end all'interno della chiusura, e quindi il programma si riavvolge per sempre.

Per quanto ho capito, dal momento che input viene creato nella funzione principale e la chiusura è memorizzata in input, non è possibile che sopravviva la funzione corrente. C'è un modo per esprimere a Rust che la chiusura non sopravviverà a main? O c'è la possibilità che non riesca a vedere che la chiusura sopravviverà a main? In quest'ultimo caso, c'è un modo per costringerlo a vivere solo fino a quando main?

Devo refactoring il modo in cui gestisco l'input, o c'è un modo in cui posso farlo funzionare. Se ho bisogno di un refactoring, dove posso cercare di vedere un buon esempio di questo in Rust?

Ecco a playpen of a simplified version. È possibile che ho commesso un errore che potrebbe causare l'arresto anomalo del browser. Mi è capitato una volta, quindi, attenzione.

Nel caso sia necessario, il resto di my code is available. Tutte le informazioni pertinenti devono essere in main.rs o input.rs.

risposta

8

Il problema non è la chiusura, ma il metodo add_handler. Completamente ampliato sarebbe simile a questa:

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

Come potete vedere, c'è un implicito 'static legato sull'oggetto trait. Ovviamente non vogliamo che, quindi introduciamo una seconda vita 'b:

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

Dal momento che si sta aggiungendo l'oggetto handler al campo Input::handlers, quel campo non può sopravvivere la portata dell'oggetto handler. Così abbiamo anche bisogno di limitare la sua vita:

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

Ciò richiede di nuovo l'impl di avere una vita, che possiamo utilizzare nel metodo add_handler.

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

Ora tutto quello che resta è utilizzando un Cell per controllare l'accesso al tuo should_end bandiera.

+0

ho solo bisogno di leggere su 'Cell' e così via. Altrimenti, risposta molto approfondita, grazie. – MrNosco

+0

Da dove proviene questo '' static' implicito, è documentato da qualche parte? – starblue

+0

iirc se non si specifica una durata è sempre ''statico' eccetto in argomenti di funzione e binding locali, che possono ovviamente inferire periodi di vita più brevi –

0

Ecco 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); 
    } 
}