Seguro que puede:
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);
}
Como se trata de Rust, debe tener en cuenta la propiedad y la vida útil del cierre .
TL; DR; Básicamente, existen 3 tipos de cierres (objetos invocables):
Fn
: No puede modificar los objetos que captura.
FnMut
: Puede modificar los objetos que captura.
FnOnce
: El más restringido. Solo se puede llamar una vez porque cuando se llama se consume a sí mismo y sus capturas.
Consulte ¿ Cuándo implementa un cierre Fn, FnMut y FnOnce? para más detalles
Si está utilizando un cierre simple de puntero a función, entonces el conjunto de captura está vacío y tiene el Fn
sabor.
Si quieres hacer cosas más sofisticadas, entonces tendrás que usar funciones lambda.
En Rust hay punteros adecuados para funciones, que funcionan igual que los de C. Su tipo es, por ejemplo fn(i32) -> i32
. El Fn(i32) -> i32
, FnMut(i32) -> i32
y FnOnce(i32) -> i32
en realidad son rasgos. Un puntero a una función siempre implementa los tres, pero Rust también tiene cierres, que pueden o no convertirse en punteros (dependiendo de si el conjunto de captura está vacío) a funciones, pero implementan algunos de estos rasgos.
Entonces, por ejemplo, el ejemplo anterior se puede expandir:
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*
son rasgos, por lo que se aplica el<T: Trait>
vs habitual(t: &T)
. La principal limitación de la solución no genérica es que debe usarse con referencias. Entonces, si lo deseaFnOnce
, lo que debe pasar como una copia, debe usar el estilo genérico.<F: Fn..>
lugar de(f: &Fn...)
. Y esto es por una razón: los genéricos darán como resultado un envío estático, mientras que los objetos de rasgo requieren un envío dinámico.FnOnce
es en realidad el rasgo más genérico: acepta todos los cierres independientemente de si leen, modifican o toman posesión del estado capturado.FnMut
es más restrictivo, no acepta cierres que se apropien de un objeto capturado (pero aún permite modificaciones de estado).Fn
es el más restrictivo porque no acepta cierres que modifiquen su estado capturado. Por lo tanto, requerir&Fn
coloca la mayor restricción para lafunTest
persona que llama, al tiempo que proporciona la menor restricción sobre cómof
se puede invocar dentro de ella.Fn
,FnMut
YFnOnce
, se indica en la otra respuesta, son de cierre tipos. Los tipos de funciones que se cierran sobre su alcance.Además de pasar cierres, Rust también admite pasar funciones simples (sin cierre), como esta:
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
aquí hay un tipo de puntero de función .Si no necesita un cierre completo, trabajar con tipos de funciones suele ser más sencillo, ya que no tiene que lidiar con esas bondades de por vida del cierre.
fuente