Skip to content

Commit d4835a4

Browse files
committed
blog post for 0.22 release
1 parent 9cc47a0 commit d4835a4

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
layout: post
3+
title: 0.22 — async methods
4+
author: dwrensha
5+
---
6+
7+
Today I have published version 0.22.0 of the
8+
[capnp](https://crates.io/crates/capnp),
9+
[capnpc](https://crates.io/crates/capnpc),
10+
[capnp-futures](https://crates.io/crates/capnp-futures),
11+
and [capnp-rpc](https://crates.io/crates/capnp-rpc),
12+
crates.
13+
14+
The new release enables idiomatic usage of
15+
`async` and `await` in RPC method implementations,
16+
taking advantage of the
17+
"[return position impl Trait in traits](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html)"
18+
feature stabilized in Rust 1.75. The relevant changes landed in pull request
19+
[#593](https://github.com/capnproto/capnproto-rust/pull/593).
20+
Thank you [@luisholanda](https://github.com/luisholanda) for
21+
the contribution!
22+
23+
<!-- https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits/ -->
24+
25+
## how it was before
26+
27+
28+
Suppose we had a capnp schema file `example.capnp` declaring the following interface:
29+
30+
```
31+
interface Foo {
32+
bar @0 (x :UInt16) -> (y :UInt16);
33+
}
34+
```
35+
36+
In previous versions, capnpc-rust would generate a Rust trait with the following signature:
37+
38+
```rust
39+
pub mod foo {
40+
trait Server {
41+
fn bar(&mut self, foo::BarParams, foo::BarResults) -> Promise<(), Error>;
42+
}
43+
// ...
44+
}
45+
```
46+
47+
The return type `Promise<(), Error>` allowed implementations of the method to
48+
use asynchronous code, like this:
49+
```rust
50+
Promise::from_future(async move {
51+
// ... do some asynchronous stuff here
52+
})
53+
```
54+
55+
However, there were some problems with this setup:
56+
57+
1. Rust users accustomed to `async/await` [found it annoying](https://github.com/capnproto/capnproto-rust/issues/168)
58+
to need to manually construct a `Promise` like this.
59+
60+
2. The `?` operator did not work inside such a method body without enabling the unstable `try_trait_v2` feature.
61+
62+
3. There was no way to get access to the borrowed `&mut self` parameter from within the `async` context, because `Promise::from_future()` requires that its input be `'static`.
63+
64+
65+
## the new way
66+
67+
Starting in version 0.22.0, the generated trait has the following signature:
68+
69+
```rust
70+
pub mod foo {
71+
trait Server {
72+
fn bar(&self,
73+
foo::BarParams,
74+
foo::BarResults) -> impl Future<Output=Result<(), Error>> + '_;
75+
}
76+
// ...
77+
}
78+
79+
```
80+
81+
Now user-provided implementations can use the `async fn` syntax like this:
82+
83+
```rust
84+
85+
impl foo::Server for FooImpl {
86+
async fn bar(&self, foo::BarParams, foo::BarResults) -> Result<(), Error> {
87+
// ... do some asynchronous stuff here, possibly using the `?` operator,
88+
// and possibly using &self after an await point.
89+
}
90+
}
91+
```
92+
93+
This solves all three of the problems noted above!
94+
95+
## the price of progress
96+
97+
This is a breaking change. All implementations of RPC traits will need to be
98+
updated to account for the change in method signature.
99+
100+
In particular, because the `&mut self` parameter has changed to
101+
an immutable `&self` parameter, RPC objects may need to add their own
102+
interior mutability. This can typically be done by wrapping
103+
data in `Cell<T>` or `RefCell<T>`.
104+
105+
Moreover, getting this all to work required adding some `async/await` in
106+
the generated method dispatch code. Therefore, capnpc-rust no longer
107+
supports generating code for Rust editions 2015 and 2018.

0 commit comments

Comments
 (0)