Avant Rust 1.0, je pouvais écrire une structure en utilisant cette syntaxe de fermeture obsolète:
struct Foo {
pub foo: |usize| -> usize,
}
Maintenant, je peux faire quelque chose comme:
struct Foo<F: FnMut(usize) -> usize> {
pub foo: F,
}
Mais alors quel est le type d'un objet Foo
que je crée?
let foo: Foo<???> = Foo { foo: |x| x + 1 };
Je pourrais également utiliser une référence:
struct Foo<'a> {
pub foo: &'a mut FnMut(usize) -> usize,
}
Je pense que c'est plus lent car
FnMut
qui finit par être utiliséLes fermetures à l'ancienne qui utilisaient la syntaxe ||
Faisaient référence à des fermetures stockées sur la pile, ce qui les rend équivalentes à &'a mut FnMut(usize) -> usize
. Les anciens proc
s étaient alloués en tas et équivalaient à Box<dyn FnOnce(usize) -> usize>
(vous ne pouvez appeler une proc
qu'une seule fois).
Quant au type que vous utiliseriez dans votre troisième extrait de code, il n'est pas un; les types de fermeture sont anonymes et ne peuvent pas être directement nommés. Au lieu de cela, vous écririez:
let foo = Foo { foo: |x| x + 1 };
Si vous écrivez du code dans un contexte où vous besoin pour spécifier que vous voulez un Foo
, vous écririez:
let foo: Foo<_> = Foo { foo: |x| x + 1 };
Le _
Indique au système de types de déduire le type générique réel pour vous.
La règle générale pour qui à utiliser, dans l'ordre décroissant:
struct Foo<F: FnMut(usize) -> usize>
. C'est le plus efficace, mais cela signifie qu'une instance Foo
spécifique ne peut stocker que one fermeture, car chaque fermeture a un type de béton différent.&'a mut dyn FnMut(usize) -> usize
. Il y a une indirection de pointeur, mais maintenant vous pouvez stocker une référence à toute fermeture qui a une signature d'appel compatible.Box<dyn FnMut(usize) -> usize>
. Cela implique d'allouer la fermeture sur le tas, mais vous n'avez pas à vous soucier des durées de vie. Comme pour une référence, vous pouvez stocker n'importe quelle fermeture avec une signature compatible.Compléter la réponse existante avec un peu plus de code à des fins de démonstration:
Utilisez un type générique:
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
}
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
}
struct Foo<'a> {
pub foo: &'a dyn Fn(usize) -> usize,
}
fn main() {
let foo = Foo { foo: &|a| a + 1 };
(foo.foo)(42);
}
struct Foo {
pub foo: fn(usize) -> usize,
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
}
quel est le type d'un objet
Foo
que je crée?
C'est un type innommable, généré automatiquement.
Je pourrais aussi utiliser une référence [...] plus lente car le pointeur [...] ne deref aucune spécialisation
Peut-être, mais cela peut être beaucoup plus facile pour l'appelant.
Voir également: