Erase Work, Preserve Results
One MPSC channel has one item type, but submissions may return unrelated types:
Future<Output = Result<User, HttpError>>
Future<Output = Result<u64, DbError>>
The runner erases the wrapper’s output to ():
type ErasedJob = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
Inside submit, the typed future and typed sender are captured together:
let (result_tx, result_rx) = oneshot::channel();
let erased = Box::pin(async move {
let result: Result<T, E> = job.await;
let _ = result_tx.send(result);
});
work_sender.send(erased).await?;
return JobHandle { receiver: result_rx };
The queue sees only Future<Output = ()>. The type relationship between T,
E, and that caller’s handle remains inside the captured sender/receiver pair.
We erase what the shared collection must homogenize and preserve what the
caller needs.
The bounds explain ownership:
'static: queued work cannot borrow a caller stack frame that may disappear;Send: Tokio may move the future, result, or error between worker threads;- no
Syncis required merely to transfer exclusive ownership.