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

bfops picture bfops · Jan 8, 2015 · Viewed 21k times · Source

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

Answer

Shepmaster picture Shepmaster · Oct 22, 2018

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: