I'm trying to create a struct that will manage a Tokio task with one tokio::sync::mpsc::Sender
that sends input to the task, one tokio::sync::mpsc::Receiver
that receives output from the task, and a handle that I can join at the end.
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
// A type that implements BlockFunctionality consumes instances of T and
// produces either Ok(Some(U)) if an output is ready, Ok(None) if an output
// is not ready, or an Err(_) if the operation fails
pub trait BlockFunctionality<T, U> {
fn apply(&mut self, input: T) -> Result<Option<U>, &'static str>;
}
pub struct Block<T, U> {
pub tx_input: mpsc::Sender<T>,
pub rx_output: mpsc::Receiver<U>,
pub handle: JoinHandle<Result<(), &'static str>>,
}
impl<T: Send, U: Send> Block<T, U> {
pub fn from<B: BlockFunctionality<T, U> + Send>(b: B) -> Self {
let (tx_input, mut rx_input) = mpsc::channel(10);
let (mut tx_output, rx_output) = mpsc::channel(10);
let handle: JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
let mut owned_b = b;
while let Some(t) = rx_input.recv().await {
match owned_b.apply(t)? {
Some(u) => tx_output
.send(u)
.await
.map_err(|_| "Unable to send output")?,
None => (),
}
}
Ok(())
});
Block {
tx_input,
rx_output,
handle,
}
}
}
When I try to compile this, I get this error for B
and a similar one for the other two type parameters:
|
22 | pub fn from<B: BlockFunctionality<T, U> + Send>(b:B) -> Self {
| -- help: consider adding an explicit lifetime bound...: `B: 'static +`
...
27 | let handle:JoinHandle<Result<(), &'static str>> = tokio::spawn(async move {
| ^^^^^^^^^^^^ ...so that the type `impl std::future::Future` will meet its required lifetime bounds
I'm having a hard time understanding where the problem is with lifetimes. The way I understand it, lifetime problems usually come from references that don't live long enough, but I'm moving values, not using references. I move b
, rx_input
, and tx_output
into the closure and I keep tx_input
, rx_output
, and handle
in the calling scope. Does anyone know how to satisfy the compiler in this case?
Those values may be references or contain references. Reference types are valid types:
B
could be&'a str
. OrB
could beSomeType<'a>
, a type with a lifetime parameter, that itself contains a&'a str
.To say that
B: 'static
means that all lifetime parameters ofB
outlive'static
(ref). For example, types which own their own data and thus have no lifetime parameters (e.g.String
) satisfy this bound. But&'static str
also satisfies the bound.Because
tokio::spawn
creates something whose lifetime is not statically scoped, it requires a'static
argument.So to satisfy the compiler, add the
'static
bounds: