It will be 3 years since I started to code in Rust. I had started to code 6 years ago, so half of my career have been dedicated to write code in Rust. Over recent years, I organised a Rust conference happened in Japan, and I'm now organising RustFest Global with amazing organisers (big thank you to Flaki, Jan-Erik, and other warm people. We're now working hard, stay tuned on Nov 7th-8th!).
I decided to jot down about Rust 2021. My expectations for the future of Rust are the following:
- Support async function in
trait
(andimpl
). - Describe the difference between tokio and async-std more clear.
- Work on more materials for intermediate / advanced Rustaceans.
Support async function in trait
(and impl
)
Refs to Support `async fn` in trait methods (async/await follow-up) · Issue #2739 · rust-lang/rfcs · GitHub.
Now the Rust users try to implement the following code, we should use async-trait
crate. For instance, the following one will be compile error.
trait AsyncTrait { async fn f() { println!("Couldn't compile"); } }
Then we will get an error:
async-trait-sandbox is 📦 v0.1.0 via 🦀 v1.44.0 on ☁️ ap-northeast-1 ❯ cargo check Checking async-trait-sandbox v0.1.0 error[E0706]: functions in traits cannot be declared `async` --> src/main.rs:8:5 | 8 | async fn f() { | ^---- | | | _____`async` because of this | | 9 | | println!("Couldn't compile"); 10 | | } | |_____^ | = note: `async` trait functions are not currently supported = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
To avoid this error for a meantime, we should add a crate async-trait
. Add the following line to Cargo.toml
.
[dependencies] async-trait = "0.1.36"
Add #[async_trait]
attribute then we will be able to pass the strict compiler :)
use async_trait::async_trait; #[async_trait] trait AsyncTrait { async fn f() { println!("Could compile"); } }
async-trait
is an awesome crate that enable us to async
code being more flexible. However, I want Rust to support async fn
in trait
.
In developing web applications, we're used to build them with Dependency Injection (DI). When we're to use DI, interfaces and implementations are decoupled by using trait
. For instance, I'm usually architecting components that fetch data from persistence layers as bellow:
#[async_trait] pub trait TodoRepository { fn get(&self, id: String) -> Result<Option<Todo>, AppError>; fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>; fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>; fn delete(&mut self, id: String) -> Result<(), AppError>; } #[async_trait] impl TodoRepository for TodoRepositoryImpl { fn get(&self, id: String) -> Result<Option<Todo>, AppError> { // implementation } fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> { // implementation } fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> { // implementation } fn delete(&mut self, id: String) -> Result<(), AppError> { // implementation } }
The #[async_trait]
attribute needs to set both on trait
and impl
but that's a bit noisy. I wanna define async fn
directly as below.
pub trait TodoRepository { async fn get(&self, id: String) -> Result<Option<Todo>, AppError>; async fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>; async fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>; async fn delete(&mut self, id: String) -> Result<(), AppError>; } impl TodoRepository for TodoRepositoryImpl { async fn get(&self, id: String) -> Result<Option<Todo>, AppError> { // implementation } async fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> { // implementation } async fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> { // implementation } async fn delete(&mut self, id: String) -> Result<(), AppError> { // implementation } }
I feel it more natural in case I use async fn
than if use the handy crate.
Describe the difference between tokio and async-std more clear
Every time I introduce Rust to my coworkers (they're web developers as well), I always feel tough to describe the difference between tokio and async-std because there seems to be no clear description or documentation regarding the difference of two runtimes. I can find related posts on Reddit, on the other hand can't find official statements.
Work on more materials for intermediate / advanced Rustaceans
Move on to the next topic. The next one is about Rust books and guides.
Now we have a lot of introduction guides thanks to the community and enthusiastic users but mostly for beginners. We have few kinda advanced guide we can find out the same in other languages as "Effective XXX".
As mentioned in the Rust survey 2019 results, those who have finished "The Rust Programming Language" and used Rust for a long time seem to hope to read advanced material.
The topic will be about (these are a homage to "Effective Python" lol):
- Rustic Thinking (how to design Rust application with
trait
andstruct
, tackling onclone()
, hacking with smart pointers, and so on) - Functions (introduce some useful functions)
- Testing and debugging (mocking, integration tests and how to use gdb on Rust, and so on)
- Speeding up our Rust application
That's just a thought but looks good doesn't it?
That's it
That's it. Good luck everyone!