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(())
}
}