How do I store a closure in a struct in Rust?

ClosuresRust

Closures Problem Overview


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

  1. the pointer dereference
  2. there's no specialization for the type of FnMut that actually ends up being used

Closures Solutions


Solution 1 - Closures

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:

Solution 2 - Closures

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 specific Foo 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 procs were heap-allocated and were equivalent to Box<dyn FnOnce(usize) -> usize> (you can only call a proc once).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbfopsView Question on Stackoverflow
Solution 1 - ClosuresShepmasterView Answer on Stackoverflow
Solution 2 - ClosuresDK.View Answer on Stackoverflow