Is it possible to pass a struct method call as closure?

Hi all,

I have an scenario where I'm trying to dispatch a serde deserialized object to a method inside the same struct receiving the json string (and deserializing it to object) that can handle that object type.

The use case is the following:

  • I have a struct that actively waits for json-formatted string to be received via tokio::channel.
  • When a message arrives through the channel, I try to deserialize it to the corresponding struct type.
  • Once this is done, I would like to call a method of the current struct that has as a parameter a smart-pointer that contains a reference to the json-deserialized object.

To fullfill this, I've tried to create a property of type Hashmap where I map a string that rerpesents the object ype and link it to a closure that points to a local struct method.

list_of_subscriptions: HashMap<String, fn(Arc<dyn IEvent>)>

But this seems to not be possible, as the closure captures the reference to self and this is not allowed.

use serde::{Deserialize, Serialize};
use ram_message_base::{EventBase, IEvent};

#[derive(Serialize, Deserialize)]
pub struct WorkOrderCreated{
    pub base: EventBase,
    work_order_id: String, 
    assay: String
}

impl WorkOrderCreated{
    pub fn new(origin: &str, version: &str, name: &str, work_order_id: String, assay: String) -> Self{
        Self { base: EventBase::new(origin, version, name, "payload here"), work_order_id, assay }
    }
    
    pub fn get_work_order_id(&self) -> &String{
        &self.work_order_id
    }
    
    pub fn get_assay(&self) -> &String{
        &self.assay
    }
}

impl IEvent for WorkOrderCreated{
    fn get_message_type_name(&self) -> &str {
        "WorkOrderCreated"
    }
}

When trying to register a clouse pointing to the method that can handle this concrete message like this:

fn register_local_subscriptions(&mut self){
        let closure = |message: Arc<WorkOrderCreated>| { self.handle_work_order_created(message)};
        self.list_of_subscriptions.insert(String::from("WorkOrderCreated"), closure);
    }

I get the error:

expect fn pointer, found closure.
note: closures can only be coerced to fn types if they do not capture any variables

Is this use case just not possible at all or am I missing something fundamental to make it work?

Thanks!

As you found, you can't use type fn because the closure captures variables. Therefore you have to use Fn, FnMut or FnOnce. To put these into a HashMap you need to use Box<dyn Fn(Arc<dyn IEvent>)> (or FnMut or FnOnce) as the value type.

1 Like

Oh that's exactly what I was missing. Despite me reading the book, I could not mentally link the Fn, FnOnce and FnMut traits in this context. Thanks!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.