web-dev-qa-db-fra.com

Pourquoi le passage d'une fermeture à une fonction qui accepte un pointeur de fonction ne fonctionne-t-il pas?

Dans le deuxième édition de Le Rust langage de programmation (accent sur le mien):

Les pointeurs de fonction implémentent les trois traits de fermeture (Fn, FnMut et FnOnce), afin que vous puissiez toujours passer un pointeur de fonction comme argument pour une fonction qui attend une fermeture . Il est préférable d'écrire des fonctions en utilisant un type générique et l'un des traits de fermeture afin que vos fonctions puissent accepter des fonctions ou des fermetures.

Passer une fermeture à une fonction qui accepte un pointeur de fonction comme argument ne compile pas:

fn main() {
    let a = String::from("abc");

    let x = || println!("{}", a);

    fn wrap(c: fn() -> ()) -> () {
        c()
    }

    wrap(x);
}
error[E0308]: mismatched types
  --> src/main.rs:10:10
   |
10 |     wrap(x);
   |          ^ expected fn pointer, found closure
   |
   = note: expected type `fn()`
              found type `[closure@src/main.rs:4:13: 4:33 a:_]`

Pourquoi ça ne marche pas?

15
周汉成

Une fermeture n'est pas une fonction.

Vous pouvez passer une fonction à une fonction en attendant une fermeture, mais il n'y a aucune raison pour que l'inverse soit vrai.

Si vous voulez pouvoir passer à la fois les fermetures et les fonctions comme argument, préparez-le simplement pour les fermetures.

Par exemple:

let a = String::from("abc");

let x = || println!("x {}", a);

fn y() {
    println!("y")
}

fn wrap(c: impl Fn()) {
    c()
}

wrap(x); // pass a closure
wrap(y); // pass a function
11
Denys Séguret

Dans certains cas, vous pouvez passer une fermeture en tant que pointeur de fonction. Cela marche:

fn main() {
    let x = || {
        let a = String::from("abc");
        println!("{}", a);
    };

    fn wrap(c: fn()) {
        c()
    }

    wrap(x);
}

La différence importante est que la fermeture n'est pas autorisée à capturer n'importe quoi à partir de son environnement. Cela signifie que nous devions empêcher le String de traverser la frontière de fermeture.

Toute fermeture qui ne capture pas l'environnement peut être facilement réécrite en tant que fonction autonome anonyme, puis convertie en pointeur de fonction.

Une fois que vous ajoutez un environnement, il n'est plus convertible et tout de la réponse existante s'applique.

Notez que déclarer -> () n'est pas idiomatique car c'est la valeur par défaut si rien n'est spécifié.

Voir également:

10
Shepmaster