Why is the future not implemented Send?

Consider this example:


use std::future::Future;

trait HealthCheck:Send {
    async fn check(self) -> bool;

fn spawn<F:Future>(f:F) where F:Send + 'static{}

fn start_health_check<H>(h:H)
    H: HealthCheck + Send + 'static,
    spawn(async move {
        let f = h.check();

The compiler says

future is not Send as it awaits another future which is not Send, await occurs here on type impl Future<Output = bool>, which is not Send

However, we say H satisfies Send, hence h is sendable. In the block, f is an opaque future type impl Future<Output = bool>, the compiler says this type does not implement Send. However, the future generated by HealthCheck::check almost only captures self which has been required to satisfy Send, why is the future not sendable? In contrast to the following example

async fn senable_one(v:i32)->bool{  // The future generated by this function is sendable
struct D;
impl D{
   async fn also_sendable(&self)->bool{ // The future generated by this method is sendable

The futures in above are both sendable since i32 and D(and &D) are sendable. What reason does make the generated future from the trait method to be not sendable?

You have misunderstanding on the condition of Future being Send.

// `Foo` is `Send`
struct Foo(std::sync::Mutex<i32>);

impl HealthCheck for Foo {
   // This future is not `Send`
   async fn check(self) -> bool {
      // `guard` is not Send and it crossed an await point.
      let guard = self.0.lock().unwrap();
      async {}.await;
      *guard == 0

So, how to correctly determine whether a future is sendable or not?

  1. If no !Send value is captured or crossed an await point, then the Future is Send.

  2. There's no guarantee that all implementation of HealthCheck::check returns a Send Future, so you can't depends on it's Send in a generic context.

Do you mean we might use an unsendable value in the check method of the implementations of HealthCheck, so the compiler cannot assume all futures returned by check of implementations of HealthCheck are sendable?

Yes. Exactly. If you need more info on this specific topic, see the link posted by steffahn above.

Seems that this case has the same reason as that

fn spawn<F:Future>(f:F) where F:Send + 'static{}
fn show<F:Future+Default>(){
   spawn(async move{
	  let f = F::default();

The only difference is that even though every input types(i.e the types of the method's parameters) are sendable, however, the content in the method's body(in the implementation) is not guaranteed to not violate

value that is !Send is used across an await point

So, the compiler just assumes all futures generated from check are not sendable except we explicitly specify the ReturnedFuture: Send.

A Future is sendable if all the types that are either captured by the Future or alive across an .await point are sendable too. The analysis for the latter condition is not very precise though, see Tracking issue for more precise generator captures · Issue #69663 · rust-lang/rust · GitHub

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.