capnproto / capnproto-rust

Cap'n Proto for Rust
MIT License
2.03k stars 221 forks source link

Async/.await server impl #168

Open vhdirk opened 4 years ago

vhdirk commented 4 years ago

Currently, every implementation for a server method is a sync fn. Are there any plans to go towards async fns for server impls?

dwrensha commented 4 years ago

I think that would require async trait methods, and as far as I'm aware, those are not yet available. http://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/

dwrensha commented 4 years ago

For now my recommended workaround is to put an async {} block inside the trait method body.

iduartgomez commented 4 years ago

There are some workarounds around that, like the async-trait crate, still pretty cumbersome though as you can't work with anonymous futures for example.

dusty-phillips commented 4 years ago

Do you have any medium-sized examples of using an async {} block? I'm using Promise::from_future(async move {...}) but I'm getting some convoluted lifetime errors. I think it'd be easiest to start from a functional example.

dwrensha commented 4 years ago

The calculator example does some interesting things.

One pitfall to watch out for is that the &mut self parameter of RPC methods cannot be accessed inside the async block, because it only lives as long as the initial method call (i.e. not through additional poll() calls). I wonder if maybe we want to change that parameter to &'a self and have the return value be of type Box<dyn Future + 'a>. Then self could be accessible inside async, but would no longer be mutable. (We can't do the same thing with &mut self because that would preclude multiple concurrent method calls on the same object -- something that capnproto is supposed to support.)

dusty-phillips commented 4 years ago

That was the problem; Thank you. Luckily, the content that I needed was already inside an Arc, so it was easy to clone it into the interior task.

Samuel-B-D commented 4 years ago

A convenient way to write it is by using futures::future::BoxFuture.

trait SomeTrait {
   fn do_something_async() -> BoxFuture<bool>;
}

struct Foo;

impl SomeTrait for Foo {
   fn do_something_async() -> BoxFuture<bool> {
      async move {
         something_long().await;
         true
      }.boxed()
   }
}

Here's an example playground with some variations : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d5573a7842bb704110654dd9ce4acf61