How to avoid viral Send bounds/guarantees for traits?
⚓ Rust 📅 2026-04-02 👤 surdeus 👁️ 7Let's say I have a trait like this, for code that accepts some async input and produces some async output:
trait DoSomethingWithFuture {
fn do_something<F: Future>(f: F) -> impl Future<Output = F::Output>;
}
This abstractly expresses exactly what I want—the ability to transform a future somehow. But it fails when I try to use it in a multi-threaded environment, because even if the input is Send, the output isn't guaranteed to be (playground):
async fn foo() {}
fn bar<DSWF: DoSomethingWithFuture>() {
// Works
assert_send(foo());
// Doesn't work
// assert_send(DSWF::do_something(foo()));
}
I guess this makes sense—whether it would be Send depends on the particular implementation of do_something. Traits must therefore be an exception to the usual behavior where auto traits leak through RPIT functions.
Except… what do I do about it? Of course it works if I require the input be Send and guarantee that the output will be:
trait DoSomethingWithFuture {
fn do_something<F: Future + Send>(f: F) -> impl Future<Output = F::Output> + Send;
}
But now I've swung the other direction: the trait defeats the benefits of a non-multi-threaded environment if that's where I want to use it. More relevant to my purposes, for anything non-trivial you wind up needing to write Send bounds absolutely everywhere whenever you have any generic code that involves this trait. It's all over the place, including on input parameters for every async fn that might be involved. Send plus (async?) traits seems to be a very viral combination. It's like we don't have auto traits at all.
Is there a better pattern for this? For example, any way to express "the output is Send if and only if the input is"?
3 posts - 2 participants
🏷️ Rust_feed