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.
Complementing the existing answer with some more code for demonstration purposes:
Unboxed closure
Use a generic type:
Boxed trait object
Trait object reference
It's an unnameable, automatically generated type.
Perhaps, but it can be much easier on the caller.
See also:
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 wasproc
s that were heap-allocated, and were equivalent toBox<dyn FnOnce(uint) -> uint>
(you can only call aproc
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:
If you're writing code in a context where you need to specify that you want a
Foo
, you'd write: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:
struct Foo<F: FnMut(uint) -> uint>
. This is the most efficient, but it does mean that a specificFoo
instance can only ever store one closure, since every closure has a different concrete type.&'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.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.