I used to have something like:
struct Foo {
pub foo: |uint| -> uint,
}
Now the closure syntax is obsolete. I can do something like:
struct Foo<F: FnMut(uint) -> uint> {
pub foo: F,
}
But then what's the type of a Foo
object I create?
let foo: Foo<???> = Foo { foo: |x| x + 1 };
I could also use a reference:
struct Foo<'a> {
pub foo: &'a mut FnMut(uint) -> uint
}
But I think it's slower because a) the pointer deref, and b) now there's no specialization for the type of FnMut
that actually ends up being used.
This is wrong: The direct equivalent would be Box<dyn FnMut(uint) -> uint>
. This is effectively what the old syntax actually meant.
Correction: As dbaupp pointed out, that isn't correct. Old-style closures that used the ||
syntax were references to closures stored on the stack, making them equivalent to &'a mut FnMut(uint) -> uint
. It was proc
s that were heap-allocated, and were equivalent to Box<dyn FnOnce(uint) -> uint>
(you can only call a proc
once). My apologies for the mistake.
As for what type you'd use in your third code snippet, there isn't one; closure types are anonymous and cannot be directly named. Instead, you'd write:
let foo = Foo { foo: |x| x + 1 };
If you're writing code in a context where you need to specify that you want a Foo
, you'd write:
let foo: Foo<_> = Foo { foo: |x| x + 1 };
The _
tells the type system to infer the actual generic type for you.
The general rule of thumb as to which to use, in descending order:
- Generic parameters:
struct Foo<F: FnMut(uint) -> uint>
. This is the most efficient, but it does mean that a specific Foo
instance can only ever store one closure, since every closure has a different concrete type.
- Trait references:
&'a mut FnMut(uint) -> uint
. There's a pointer indirection, but now you can store a reference to any closure that has a compatible call signature.
- Boxed closures:
Box<dyn FnMut(uint) -> uint>
. This involves allocating the closure on the heap, but you don't have to worry about lifetimes. As with a reference, you can store any closure with a compatible signature.
Complementing the existing answer with some more code for demonstration purposes:
Unboxed closure
Use a generic type:
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
}
Boxed trait object
struct Foo {
pub foo: Box<Fn(usize) -> usize>,
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
}
Trait object reference
struct Foo<'a> {
pub foo: &'a Fn(usize) -> usize,
}
fn main() {
let foo = Foo { foo: &|a| a + 1 };
(foo.foo)(42);
}
what's the type of a Foo
object I create?
It's an unnameable, automatically generated type.
I could also use a reference [...] slower because [...] the pointer deref [...] no specialization
Perhaps, but it can be much easier on the caller.
See also:
- How do I call a function through a member variable?
- Returning a closure from a function
- How to return an anonymous type from a trait method without using Box?
- Closures as a type in a Rust struct
- Types of unboxed closures being unique to each