How do I store a closure in a struct in Rust?
Solution 1
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(usize) -> usize>
. 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. - Trait references:
&'a mut dyn FnMut(usize) -> usize
. 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(usize) -> usize>
. 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.
Before Rust 1.0
Closures that used the ||
syntax were references to closures stored on the stack, making them equivalent to &'a mut FnMut(usize) -> usize
. Old-style proc
s were heap-allocated and were equivalent to Box<dyn FnOnce(usize) -> usize>
(you can only call a proc
once).
Solution 2
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,
}
impl<F> Foo<F>
where
F: Fn(usize) -> usize,
{
fn new(foo: F) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
Boxed trait object
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
impl Foo {
fn new(foo: impl Fn(usize) -> usize + 'static) -> Self {
Self { foo: Box::new(foo) }
}
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
Trait object reference
struct Foo<'a> {
pub foo: &'a dyn Fn(usize) -> usize,
}
impl<'a> Foo<'a> {
fn new(foo: &'a dyn Fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: &|a| a + 1 };
(foo.foo)(42);
(Foo::new(&|a| a + 1).foo)(42);
}
Function pointer
struct Foo {
pub foo: fn(usize) -> usize,
}
impl Foo {
fn new(foo: fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).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
- Why does passing a closure to function which accepts a function pointer not work?
- What does "dyn" mean in a type?
bfops
Updated on July 05, 2022Comments
-
bfops almost 2 years
Before Rust 1.0, I could write a structure using this obsolete closure syntax:
struct Foo { pub foo: |usize| -> usize, }
Now I can do something like:
struct Foo<F: FnMut(usize) -> usize> { 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(usize) -> usize, }
I think this is slower because
- the pointer dereference
- there's no specialization for the type of
FnMut
that actually ends up being used
-
CMCDragonkai about 8 yearsIs it possible to create functions like
compose
orapply
that work generically over the 3 different traitsFn
,FnMut
andFnOnce
? I find myself having to create 3 different versions ofcompose
orapply
to satisfy the 3 traits of closures. -
DK. about 8 years@CMCDragonkai If you need more details, you should ask a new question, but no, I don't believe it is. Keep in mind, however, that all
Fn
s can be used asFnMut
s, and allFn
s andFnMut
s can be used asFnOnce
s. -
DanielV over 4 yearsAre any of these choices known to be speedier than the others?
-
Shepmaster over 4 years@DanielV yes, and no. If one of them was guaranteed to be universally better than the others, we wouldn't need the rest. The unboxed closure is probably the default choice and is likely to be the most performant, but the other ones can generate smaller code which might lead to them being more performant.
-
pm100 almost 4 yearsrust really needs something like std::function, c++ uniform container for anything invokable.
-
pm100 almost 4 yearswhat does that "+ 'static" mean in the boxed trait one
-
Shepmaster almost 4 years
-
Ali Somay over 3 yearsIn the
Unboxed Closure
strategy is it possible to capture the struct which the closure is a member of ? -
Shepmaster over 3 years@AliSomay Not to capture it, no, but you can pass it as an argument to the the closure itself in certain cases. See How to use struct self in member method closure; How can I modify self in a closure called from a member function?
-
Konrad almost 3 yearsWhat are advantages and disadvantages of each approach?