Sicher kannst du:
fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn times2(value: i32) -> i32 {
2 * value
}
fn main() {
fun_test(5, ×2);
}
Da dies Rust ist, müssen Sie das Eigentum und die Lebensdauer der Schließung berücksichtigen .
TL; DR; Grundsätzlich gibt es 3 Arten von Verschlüssen (aufrufbare Objekte):
Fn
: Die erfassten Objekte können nicht geändert werden.
FnMut
: Es kann die erfassten Objekte ändern.
FnOnce
: Am meisten eingeschränkt. Kann nur einmal aufgerufen werden, da es sich selbst und seine Erfassungen verbraucht, wenn es aufgerufen wird.
Siehe Wann implementiert ein Abschluss Fn, FnMut und FnOnce? für mehr Details
Wenn Sie einen einfachen Zeiger auf eine Funktion wie das Schließen verwenden, ist das Erfassungsset leer und Sie haben den Fn
Geschmack.
Wenn Sie mehr ausgefallene Sachen machen wollen, müssen Sie Lambda-Funktionen verwenden.
In Rust gibt es richtige Zeiger auf Funktionen, die genau wie in C funktionieren. Ihr Typ ist zum Beispiel fn(i32) -> i32
. Die Fn(i32) -> i32
, FnMut(i32) -> i32
und FnOnce(i32) -> i32
sind tatsächlich Züge. Ein Zeiger auf eine Funktion implementiert immer alle drei, aber Rust hat auch Verschlüsse, die in Zeiger (abhängig davon, ob der Erfassungssatz leer ist) auf Funktionen konvertiert werden können oder nicht, aber einige dieser Merkmale implementieren.
So kann beispielsweise das Beispiel von oben erweitert werden:
fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn fun_test_ptr(value: i32, f: fn(i32) -> i32) -> i32 {
println!("{}", f(value));
value
}
fn times2(value: i32) -> i32 {
2 * value
}
fn main() {
let y = 2;
fun_test_impl(5, times2);
fun_test_impl(5, |x| 2*x);
fun_test_impl(5, |x| y*x);
fun_test_dyn(5, ×2);
fun_test_dyn(5, &|x| 2*x);
fun_test_dyn(5, &|x| y*x);
fun_test_ptr(5, times2);
fun_test_ptr(5, |x| 2*x);
fun_test_ptr(5, |x| y*x);
}
Fn*
sind Züge, so die übliche<T: Trait>
vs(t: &T)
gilt. Die Hauptbeschränkung der nicht generischen Lösung besteht darin, dass sie mit Referenzen verwendet werden muss. Wenn Sie also möchtenFnOnce
, was als Kopie übergeben werden soll, müssen Sie den generischen Stil verwenden.<F: Fn..>
anstelle von(f: &Fn...)
. Und dies aus einem Grund - Generika führen zu einem statischen Versand, während Merkmalsobjekte einen dynamischen Versand erfordern.FnOnce
das allgemeinste Merkmal - es akzeptiert alle Schließungen, unabhängig davon, ob sie den erfassten Status lesen, ändern oder in Besitz nehmen.FnMut
ist restriktiver, akzeptiert keine Schließungen, die den Besitz eines erfassten Objekts übernehmen (erlaubt jedoch weiterhin Statusänderungen).Fn
ist am restriktivsten, da es keine Schließungen akzeptiert, die ihren erfassten Status ändern. Das Erfordernis&Fn
stellt also die größte Einschränkung für denfunTest
Anrufer dar, während die geringste Einschränkung für die Art und Weise festgelegt wird, wief
in ihm aufgerufen werden kann.Fn
,FnMut
UndFnOnce
in der anderen Antwort dargelegt, sind Verschlusstypen. Die Arten von Funktionen, die sich über ihren Umfang schließen.Neben dem Übergeben von Verschlüssen unterstützt Rust auch das Übergeben einfacher (nicht schließender) Funktionen wie folgt:
fn times2(value: i32) -> i32 { 2 * value } fn fun_test(value: i32, f: fn(i32) -> i32) -> i32 { println!("{}", f (value)); value } fn main() { fun_test (2, times2); }
fn(i32) -> i32
Hier ist ein Funktionszeigertyp .Wenn Sie keinen vollwertigen Verschluss benötigen, ist die Arbeit mit Funktionstypen oft einfacher, da Sie sich nicht mit diesen Verschlusslebensdauern befassen müssen.
quelle