Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions develop-docs/engineering-practices/rust.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,26 @@ During migration you may need normal functions which return futures, for their s

### Async Traits

In **traits** you can not yet use `async fn` ([see this blog post](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/)).
In this case, functions should return `-> Pin<Box<dyn Future<Output = ...> + Send>>`:
Support for async in **traits** has [landed in Rust](https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html)
and should generally be preferred now.

```rust
trait Database {
fn get_user(&self) -> Pin<Box<dyn Future<Output = User> + Send + '_>>;
pub trait Database {
fn get_user(&self) -> impl Future<Output = User> + Send;
Copy link
Member

@jan-auer jan-auer Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanna add a callout here why we should be explicit with Send and Sync and prefer impl Future over async for that reason in trait definitions?

Personally, I'd add a recommendation to use associated types where possible (especially for Iterators!) as this is extremely useful for composition in the absence of generators, but for async there's simply no need to create named futures in most cases.

Copy link
Member Author

@Dav1dde Dav1dde Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanna add a callout here why we should be explicit with Send and Sync and prefer impl Future over async for that reason?

I kept this message, hoping it would address this clearly enough:

Note that the returned future type is Send, to ensure that it can run on a multi-threaded runtime.


Personally, I'd add a recommendation to name types where possible (especially for Iterators!) as this is extremely useful for composition in the absence of generators [...]

This requires explicit associated types until this restriction is lifted. I think for most of Sentry's (application) code this would be overkill. I can add another note that explains that as well.

}

impl Database for MyDB {
fn get_user(&self) -> Pin<Box<dyn Future<Output = User> + Send + '_>> {
Box::pin(async {
// ...
})
}
impl Database for MyDatabase {
async fn get_user(&self) -> User {
todo!()
}
}
```

Note that the returned future type is `Send`, to ensure that it can run on a multi-threaded runtime.

This corresponds to what the [async-trait crate](https://crates.io/crates/async-trait) does.
When you need dynamic dispatch or have to support Rust versions older than 1.75 consider using the
[`async-trait`](https://docs.rs/async-trait/) crate.


### Avoid `.unwrap()`

Expand Down