2016-06-21 11 views
6

Verschlüsse haben einige Daten in ihrem Zustand, aber wie mache ich es veränderbar? Zum Beispiel möchte ich einen Zähler schließen, der jedes Mal den inkrementierten Wert zurückgibt, aber es funktioniert nicht. Wie kann ich es funktionieren lassen?Wie verschiebe ich den veränderbaren Zustand in einen Abschluss?

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

Fehler (und Warnung) Ich erhalte:

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

Ich erwarte, dass es zur Ausgabe etwas wie [1, 2, 3, 1, 2, 4].

Antwort

8

Da die Fehlermeldung sagt:

nicht Statt erfasst äußere Variable in einem Fn Schließung

zuweisen können, möchten Sie einen FnMut Schließung:

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

Als FnMut Dokumente sagen:

Eine Version des Anrufoperators, die einen veränderbaren Empfänger akzeptiert.

Dadurch kann der Verschluss den enthaltenen Zustand mutieren.

Übrigens ist der explizite Typ für c nicht erforderlich.


Was mich verwirrt, ist, dass pub trait Fn<Args>: FnMut<Args>. Heißt das nicht, dass Fn (was ich benutzt habe) sollte das Verhalten von FnMut unterstützen?

Vielleicht kann When does a closure implement Fn, FnMut and FnOnce? helfen, einige Hintergrundinformationen bereitzustellen. Dies ist ein Aspekt, den ich intuitiv erfahre, aber noch nicht herausgefunden habe, wie man am besten kommunizieren kann. Dieser Abschnitt von Finding Closure in Rust scheint auch relevant:

Auf hohem Niveau self gibt Implementierer (das heißt die Typen Benutzer definieren das Merkmal zu implementieren) die größte Flexibilität, mit &mut self nächsten und &self die am wenigsten flexibel. Umgekehrt gibt &self den Verbrauchern des Merkmals (d. H. Funktionen mit Generika, die durch das Merkmal begrenzt sind) die größte Flexibilität und am wenigsten.

Kurz gesagt, dieses Merkmal Definition besagt, dass jede FnMut Schließung als Fn Verschluss verwendet werden kann. Dies ist sinnvoll, da wir die Veränderlichkeit einfach ignorieren können. Du kannst nicht den anderen Weg gehen - du kannst einen unveränderlichen Bezug nicht zu einem veränderlichen machen.

+0

Vielen Dank! Was mich verwirrt, ist das 'Pub Merkmal Fn : FnMut '. Bedeutet das nicht, dass "Fn" (was ich benutzt habe) das Verhalten von 'FnMut' unterstützen sollte? – Shchvova

+2

@Shchvova aktualisiert. – Shepmaster

Verwandte Themen