web-dev-qa-db-fra.com

Dans Rust comment passez-vous une fonction en tant que paramètre?

Puis-je passer une fonction en tant que paramètre dans Rust (probablement oui)), si cela est possible comme je peux le faire.

Si vous ne pouvez pas, ce qui est une bonne alternative.

J'ai essayé une syntaxe mais je n'ai pas encore


Je sais que je peux le faire

..//

let fun: fn(value: i32) -> i32;
fun = funTest;
fun(5i32);

..//
fn funTest(value: i32) -> i32 {
    println!("{}", value);
    value
}

mais pas comme passer la fonction en tant que paramètre à une autre fonction

..//
fn funTest(value: i32, (some_function_prototype)) -> i32 {
    println!("{}", value);
    value
}
51
Angel Angel

Sûr que vous pouvez:

fn funTest(value: i32, f: &Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    funTest(5, &times2);
}

Mais ceci est Rust, vous devez donc prendre en compte propriété et durée de vie de la fermeture .

TL; DR; Fondamentalement, il existe 3 types de fermetures (objets appelables):

  1. Fn: Le plus générique, c'est une fonction pure.
  2. FnMut: Il peut modifier les objets qu'il capture.
  3. FnOnce: Le plus restreint. Ne peut être appelé qu'une seule fois car quand il s'appelle, il se consomme et capture.

Si vous utilisez un simple pointeur sur une fonction comme la fermeture, le jeu de capture est vide et vous avez le style Fn.

Si vous voulez faire plus de choses sophistiquées, vous devrez alors utiliser les fonctions lambda.


[~ # ~] met à jour [~ # ~] : Après un peu plus de temps d'apprentissage sur Rust, j'ai besoin de l'envie de corriger un peu cette réponse.

Dans Rust, il existe des pointeurs appropriés vers des fonctions qui fonctionnent comme ceux de C. Leur type est par exemple fn(i32) -> i32. Le Fn(i32) -> i32, FnMut(i32) -> i32 et FnOnce(i32) -> i32 sont en réalité des traits. Un pointeur sur une fonction implémente toujours ces trois éléments, mais Rust a aussi des fermetures, qui peuvent ou non être convertis en pointeurs (selon que le jeu de capture est vide ou non) en fonctions, mais ils implémentent certains de ces traits.

Ainsi, par exemple, l'exemple ci-dessus peut être développé (et corrigé un peu):

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;
    //static dispatch
    fun_test_impl(5, times2);
    fun_test_impl(5, |x| 2*x);
    fun_test_impl(5, |x| y*x);
    //dynamic dispatch
    fun_test_dyn(5, &times2);
    fun_test_dyn(5, &|x| 2*x);
    fun_test_dyn(5, &|x| y*x);
    //C-like pointer to function
    fun_test_ptr(5, times2);
    fun_test_ptr(5, |x| 2*x); //ok: empty capture set
    fun_test_ptr(5, |x| y*x); //error: expected fn pointer, found closure
}
76
rodrigo

Fn, FnMut et FnOnce, décrits dans l'autre réponse, sont des types de fermeture . Les types de fonctions qui se ferment sur leur étendue.

En plus des fermetures passantes Rust supporte également les fonctions simples (non fermantes), comme ceci:

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 voici un type de pointeur de fonction .

Si vous n'avez pas besoin d'une fermeture à part entière, il est souvent plus simple de travailler avec des types de fonctions, car vous n'avez pas à gérer ces aspects de la durée de vie de la fermeture.

14
ArtemGr