-1

I want to run a function on a different thread with access to variables only available in this thread. The code below is my implementation which works fine without the borrowed parameter (&mut Executor) but when I add it I receive the following error:

error: implementation of `FnOnce` is not general enough
  --> src/in_main_loop_executor.rs:32:21
   |
32 |         self.0.send(Box::new(callback_wrapper))?;
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'2 mut in_main_loop_executor::Executor) -> Result<(), anyhow::Error>` must implement `FnOnce<(&'1 mut in_main_loop_executor::Executor,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 mut in_main_loop_executor::Executor,)>`, for some specific lifetime `'2`

Now I do understand that rust seems to have a problem assigning the proper lifetimes but I'm not sure how I would declare them to resolve this issue. I've seen other questions regarding similar issues but couldn't apply their solutions to my issue.

To run the snippet below the crates flume 0.10 and anyhow 1 are required.

use std::any::{type_name, Any};

struct Executor;

type WrappedCallback = Box<dyn FnOnce(&mut Executor) -> anyhow::Result<()> + Send + Sync>;

pub struct InMainLoopExecutor(flume::Sender<WrappedCallback>);

impl InMainLoopExecutor {
    pub fn new() -> (Self, InMainLoopExecutionWorker) {
        let (sender, receiver) = flume::bounded(50);

        (Self(sender), InMainLoopExecutionWorker(receiver))
    }

    pub async fn run_in_main_loop<T: Any + Send, C: 'static>(
        &mut self,
        callback: C,
    ) -> anyhow::Result<T>
    where
        C: FnOnce(&mut Executor) -> T + Send + Sync + 'static,
    {
        let (return_channel_sender, return_channel_receiver) =
            flume::bounded::<Box<dyn Any + Send>>(1);
        let callback_wrapper = |executor| {
            let result = callback(executor);

            return_channel_sender
                .send(Box::new(result))
                .map_err(|_| anyhow::anyhow!("Executor dropped return channel"))
        };
        self.0.send(Box::new(callback_wrapper))?;

        let result = return_channel_receiver.try_recv()?;
        let result = result.downcast::<T>().map_err(|_| {
            anyhow::anyhow!(
                "Could not downcast result to expected type {}",
                type_name::<T>()
            )
        })?;

        Ok(*result)
    }
}

pub struct InMainLoopExecutionWorker(flume::Receiver<WrappedCallback>);

impl InMainLoopExecutionWorker {
    pub fn process_callbacks(&mut self) -> anyhow::Result<()> {
        let mut executor = Executor;
        while let Ok(callback) = self.0.try_recv() {
            callback(&mut executor)?;
        }

        Ok(())
    }
}
Max Jöhnk
  • 151
  • 1
  • 9
  • Your question might be answered by these questions: https://stackoverflow.com/questions/67034393 https://stackoverflow.com/questions/70868313 But one can only be sure with a proper [mre]. The definitions of `Executor` or `WrappedCallback` are not provided. – E_net4 - Krabbe mit Hüten May 17 '22 at 08:42
  • While I haven't seen these particular questions the solution offered there did not seem to apply to my particular issue or I didn't properly understand how to apply it there. The code snippet I gave should compile just fine though, both `Executor` and `WrappedCallback` are defined at the top of the file. – Max Jöhnk May 17 '22 at 09:07
  • 1
    Replace `let callback_wrapper = |executor| {` with `let callback_wrapper = move |executor: &mut _| {` 1) the parameter type hint `&mut _` is so that the compiler can infer a HRTB on the executor's reference. This is indicated as a solution in https://stackoverflow.com/q/67482993 and https://stackoverflow.com/questions/67034393. 2) Then the `move` tells the compiler to move the callback into the closure. – E_net4 - Krabbe mit Hüten May 17 '22 at 09:54
  • I was sure that I had tried this before (I actually had the `move` keyword there before) but apparently another change I did caused this solution to fail. Thank you, if you don't want to I can add the updated snipped as the accepted answer. – Max Jöhnk May 17 '22 at 09:59

0 Answers0