Wrapping static callbacks with closures

I'm new to Rust (coming from mainly C# and Python).
So first of all: Hey y'all!

I'm using paho.mqtt to create an AsyncClient that requires static callbacks. Currently I'm using static functions but I'd like to wrap the client creation in my own MyClient struct that takes care of configuration/creation of AsyncClient and also wrap the callbacks in MyClient methods (to add additional logging, track the client status, etc.).

I've tried for a few hours but the borrow checker is giving me a hard time.

Could someone please describe a pattern to achieve that? I'm trying to do something like this:

struct Client {
    host: String,
    cli: Box<AsyncClient>,
}

fn new(host: &str) -> Client {
    let options = mqtt::CreateOptionsBuilder::new()
        .server_uri(String::from(host))
        .client_id("my_client_id")
        .finalize();

     let mut client = Client {
         host: String::from(host),
         cli: Box::new(AsyncClient::new(options).unwrap()),
     };

     client.cli.set_connection_lost_callback(move |_c: &AsyncClient| {
          println!("Connection lost");
          client.try_reconnect();
     });
     ....
     client
 }

The problem is that you are trying to move client into itself (which invalidates the variable client), which doesn't work. You can't have client borrow itself either though. The approach you should take is to call methods on the AsyncClient obtained in the closure, rather than on the outer Client. If you need host there for some reason, you could just moved an owned (i.e. String) copy of it into the closure. If you're trying to do something more complicated, then you'll need to give some more details on what you want so we can give more specific advice. Having the callbacks hold the information they need should make it work for most of what you might want to do.

You can use Arc to share the AsyncClient without references. But be aware that this might be a memory leak — if your Client is dropped, that doesn't drop the references you now have inside the closures, which keep the AsyncClient alive.

Perhaps you can use Weak inside the closures to stop them from keeping it alive indefinitely.

Thanks for your answers. I'll try using an Arc.
What I would want to, ideally, would be to bind the lifetime of the AsnyClient to MyClient struct and make sure AsyncClient is properly freed, once the scope owning MyClient ends. I tried annotating lifetimes and instantiating MyClient as static but I can't seem to resolve the conflicting lifetime requirement for the closures.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.