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
FnMut
that actually ends up being usedA closure is Clone or Copy if it does not capture any values by unique immutable or mutable reference, and if all values it captures by copy or move are Clone or Copy , respectively.
By taking &Fn(f64) , you've restricted yourself to only accepting closures which require immutable access to their context. If you want a closure to be able to mutate its context, you need to use FnMut instead.
We create an instance by stating the name of the struct and then add curly brackets containing key: value pairs, where the keys are the names of the fields and the values are the data we want to store in those fields. We don't have to specify the fields in the same order in which we declared them in the struct.
Rust uses a feature called traits, which define a bundle of functions for structs to implement. One benefit of traits is you can use them for typing. You can create functions that can be used by any structs that implement the same trait.
Complementing the existing answer with some more code for demonstration purposes:
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);
}
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);
}
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);
}
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:
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:
struct Foo<F: FnMut(usize) -> usize>
. 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.&'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.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.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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With