Use context var in struct member closure?

Viewed 63

I am not sure whether I am asking the right question. But I'll give it a shot:

I have a struct called Handlers:

pub struct Handlers {
    pub create: fn(u32) -> String,
}

Here the member named create is a closure when implemented.

So, why do I want to have a closure here?

Because on implemtation I want to use a variable from the context and I can only do that with a closure.

So, what I want to do, is this:

impl Handlers {
    fn new(name: &'static str) -> Self {
        Self {
            create: |id| {
                format!("create not implemented for name {} id {:}", name, id)
            },
        }
    }
}

When doing this I receive the error message

[rustc E0308] [E] mismatched types

expected fn pointer, found closure

note: expected fn pointer `fn(u32) -> std::string::String`
            found closure `[closure@src/handlers.rs:20:21: 25:14]`

The compiler infers a function pointer. And a function pointer is not what I need here. In order to capture the environment, it must infer a closure.

However, all is fine if I dont't use the variable "name" in the closure implementation. I.e. like this:

impl Handlers {
    fn new(name: &'static str) -> Self {
        Self {
            create: |id| {
                format!("create not implemented for id {:}", id)
            },
        }
    }
}

But I really need the variable "name" in my closure. Most likely I need to use somekind of a pointer solution, because the problem might have to do with scope.

I am still too much of a rookie to be able to wrap my head around a feasable solution.

1 Answers

The first thing to understand is that fn(u32) -> String doesn't actually represent a closure. It simply represents a function pointer.

The second thing, is that while, e.g. |x, y| x + y is a closure expression it doesn't always result in a closure. If a closure expression doesn't capture an environment, then it can represent a "plain function pointer". This is why your second example works.


Instead you'll have to use any of the Fn, FnMut, or FnOnce traits. You'll additionally have to Box the closure.

pub struct Handlers {
    pub create: Box<dyn Fn(u32) -> String>,
}

impl Handlers {
    fn new(name: &'static str) -> Self {
        Self {
            create: Box::new(move |id| {
                format!("create not implemented for name {} id {:}", name, id)
            }),
        }
    }
}
Related