2016-06-21 13 views
6

Le chiusure hanno alcuni dati nel loro stato, ma come posso renderlo mutabile? Ad esempio, voglio un contro-chiusura che restituisca il valore incrementato ogni volta, ma non funziona. Come posso farlo funzionare?Come si sposta lo stato mutabile in una chiusura?

fn counter() -> Box<Fn() -> i32> { 
    let mut c: i32 = 0; 
    Box::new(move || { 
     c += 1; 
     c 
    }) 
} 

fn main() { 
    let mut a = counter(); 
    let mut b = counter(); 
    println!("{:?}", [a(), a(), a(), b(), b(), a()]); 
} 

errore (e avvertimento) sto ottenendo:

error: cannot assign to captured outer variable in an `Fn` closure 
     c += 1; 
     ^~~~~~ 
help: consider changing this closure to take self by mutable reference 
    Box::new(move || { 
     c += 1; 
     c 
    }) 

mi aspettano che qualcosa di output come [1, 2, 3, 1, 2, 4].

risposta

8

Come il messaggio di errore dice:

non può assegnare alla variabile esterna catturata in un Fn chiusura

Invece, si vuole un FnMut chiusura:

fn counter() -> Box<FnMut() -> i32> { 
    let mut c = 0; 
    Box::new(move || { 
     c += 1; 
     c 
    }) 
} 

fn main() { 
    let mut a = counter(); 
    let mut b = counter(); 

    let result = [a(), a(), a(), b(), b(), a()]; 
    println!("{:?}", result); 

    assert_eq!([1, 2, 3, 1, 2, 4], result); 
} 

Come il FnMut documenti dicono:

Una versione dell'operatore di chiamata che accetta un ricevitore mutabile.

Ciò consente alla chiusura di mutare lo stato contenuto.

Per inciso, il tipo esplicito per c non è necessario.


Quello che mi confonde, è che pub trait Fn<Args>: FnMut<Args>. Non significa che Fn (cosa ho usato) dovrebbe supportare il comportamento di FnMut?

Forse When does a closure implement Fn, FnMut and FnOnce? può aiutare a fornire alcune informazioni di base. Questo è un aspetto che ottengo intuitivamente, ma non ho capito come meglio comunicare. Questa sezione da Finding Closure in Rust sembra anche rilevante:

ad un alto livello self dà implementatori (cioè gli utenti definiscono tipi di attuare il tratto) la massima flessibilità, con &mut self successivo e &self la meno flessibile. Al contrario, &self fornisce agli utenti il ​​tratto (ovvero funzioni con generici delimitati dal tratto) la massima flessibilità e il minimo è self.

In breve, tale definizione caratteristica dice che ogni chiusura FnMut può essere usato come un Fn chiusura. Questo ha un senso, dato che possiamo semplicemente ignorare la mutabilità. Non puoi andare dall'altra parte - non puoi rendere un riferimento immutabile in uno mutevole.

+0

Grazie! Ciò che mi confonde, è che il tratto "pub Fn : FnMut ". Non significa che 'Fn' (cosa ho usato) dovrebbe supportare il comportamento di' FnMut'? – Shchvova

+2

@Shchvova aggiornato. – Shepmaster

Problemi correlati