Oczywiście że możesz:
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);
}
Ponieważ jest to Rust, musisz wziąć pod uwagę własność i czas życia zamknięcia .
TL; DR; Zasadniczo istnieją 3 typy domknięć (obiekty wywoływalne):
Fn
: Nie może modyfikować przechwyconych obiektów.
FnMut
: Może modyfikować przechwycone obiekty.
FnOnce
: Najbardziej ograniczone. Można go wywołać tylko raz, ponieważ gdy jest wywoływany, konsumuje siebie i łapie.
Zobacz Kiedy zamknięcie implementuje Fn, FnMut i FnOnce? po więcej szczegółów
Jeśli używasz prostego wskaźnika do funkcji, takiego jak zamknięcie, zbiór przechwytywania jest pusty i masz Fn
smak.
Jeśli chcesz robić bardziej wymyślne rzeczy, będziesz musiał użyć funkcji lambda.
W Rust znajdują się odpowiednie wskaźniki do funkcji, które działają tak samo jak w C. Ich typ to na przykład fn(i32) -> i32
. Pliki Fn(i32) -> i32
, FnMut(i32) -> i32
iFnOnce(i32) -> i32
są w rzeczywistości cechy. Wskaźnik do funkcji zawsze implementuje wszystkie trzy z nich, ale Rust ma również domknięcia, które mogą, ale nie muszą, być konwertowane na wskaźniki (w zależności od tego, czy zbiór przechwytywania jest pusty) do funkcji, ale implementują one niektóre z tych cech.
Na przykład przykład z góry można rozwinąć:
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*
to cechy, więc zwykle<T: Trait>
vs(t: &T)
dotyczy. Głównym ograniczeniem rozwiązania innego niż rodzajowy jest to, że musi być używane z odniesieniami. Więc jeśli chceszFnOnce
, który powinien zostać przekazany jako kopia, musisz użyć stylu ogólnego.<F: Fn..>
Zamiast(f: &Fn...)
. Jest to powód - typy generyczne spowodują wysyłanie statyczne, podczas gdy obiekty cech wymagają dynamicznej wysyłki.FnOnce
jest to w rzeczywistości najbardziej ogólna cecha - akceptuje wszystkie zamknięcia, niezależnie od tego, czy odczytują, modyfikują, czy przejmują własność stanu przechwyconego.FnMut
jest bardziej restrykcyjny, nie akceptuje domknięć, które przejmują własność przechwyconego obiektu (ale nadal umożliwia modyfikacje stanu).Fn
jest najbardziej restrykcyjny, ponieważ nie akceptuje zamknięć, które modyfikują ich stan przechwycenia. Zatem wymaganie&Fn
nakłada największe ograniczenie nafunTest
dzwoniącego, a jednocześnie zapewnia najmniejsze ograniczenie tego, jakf
można w nim wywołać.Fn
,FnMut
IFnOnce
przedstawił w drugiej odpowiedzi, są zapinane na typy. Rodzaje funkcji, które zamykają się w ich zakresie.Oprócz przekazywania zamknięć Rust obsługuje również przekazywanie prostych (niezamykających) funkcji, takich jak:
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
tutaj jest typ wskaźnika funkcji .Jeśli nie potrzebujesz pełnoprawnego zamknięcia, praca z typami funkcji jest często prostsza, ponieważ nie musi zajmować się tymi niuansami dotyczącymi czasu życia zamknięcia.
źródło