How about this cheap but convenient complement of tokio?

I can't convince me the lazy property of Future , so I write the following little tool

pub trait FutureGo : Future {
  fn go(self)->tokio::task::JoinHandle<<Self as Future>::Output>
  where Self:'static+Send+Sized,<Self as Future>::Output:'static+Send {
    tokio::spawn(self)
  }
}

impl<T:Future> FutureGo for T {}

#[tokio::main]
async fn main() {
  let x = async {
    println!("async block 1 {:?}",thread::current().id());
  }.go();

  let y = async {
    println!("async block 2 {:?}",thread::current().id());
  }.go();
  
  x.await;
  y.await;
}

the output is

async block 1 ThreadId(2)
async block 2 ThreadId(3)

this means tokio multi thread is truly concurrent (update: this does not mean current_thread is not truly concurrent). and some times the output is

async block 2 ThreadId(3)
async block 1 ThreadId(2)

this means the tasks are pick up to run randomly

So you turned tokio::spawn into a method? I'd prefer the function notation because it's more consistent with API like thread::spawn. (And spawning tasks is pretty much the async counterpart of spawning threads in synchronous code.)

I prefer the suffix notation. You inspired me , maybe we can turn thread::spawn into .go() the same way.

It works

trait ThreadGo<T> : FnOnce<(),Output=T> {
  fn go(self)->std::thread::JoinHandle<Self::Output> 
  where Self:'static+Send+Sized,<Self as FnOnce<()>>::Output:'static+Send {
    thread::spawn(self)
  }
}

impl<T,F:FnOnce()->T> ThreadGo<T> for F {}

fn main() {
  let x = || {
    10
  };
  x.go().join();
}

this is a good practice. coding for communication!

I think it's very subjective. If you prefer it this way, it's already possible, and nothing stops you from using this trait.

If you're proposing it as an addition to tokio, then it's probably not worth it:

  • there's already task::spawn, and having more functions that do the same thing will require explaining to users that there's no difference, and choosing which one is the preferred syntax. If spawn(future) is preferred, then there's no point adding .go(). If .go() is preferred, then it will cause churn in existing code switching over, for little benefit.

  • spawning of separate tasks has wider implications (allocation cost, Send and 'static requirements, runtime dependence). It requires taking care about their cancellation. In many cases spawn is not the right thing to do. If you just need a couple things to happen at the same time, you should probably use futures::join! or join_all instead. So this adds nicer syntax to something that probably shouldn't have a syntax nicer than better options.

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.