From bc3832c73b3bfcd8f43480ee3477295068a23551 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 Sep 2024 11:38:04 -0400 Subject: [PATCH 1/5] Add return type notation call for testing post --- .../2024-09-23-rtn-call-for-testing.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 posts/inside-rust/2024-09-23-rtn-call-for-testing.md diff --git a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md new file mode 100644 index 000000000..3b67fa47e --- /dev/null +++ b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md @@ -0,0 +1,165 @@ +--- +layout: post +title: "Return Type Notation MVP: Call for Testing!" +author: Michael Goulet +team: The Async Working Group +--- + +The async working group is excited to announce that [RFC 3654] return type notation (RTN) is ready for testing. In this post, we want to briefly describe the feature and announce a call for testing on nightly Rust. + +## The backstory + +Rust 1.75 [stabilized](https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html) async fn in traits (AFIT) and return-position impl Trait in traits (RPITIT). These desugar to anonymous generic associated types (GATs). However, unlike GATs, users of these types cannot use `where` clauses to further restrict these return types. This is known as the ["send bound"](https://smallcultfollowing.com/babysteps/blog/2023/02/01/async-trait-send-bounds-part-1-intro/) problem, since it often affects `Send` bounds on futures in the async ecosystem. + +### An example + +Consider a trait `Foo` with a `method` that returns a type of `impl Future`. We want to write a function that calls `method` and spawns the future on another thread: + +```rust +fn spawn(f: impl Future + Send + 'static) {} + +trait Foo { + fn method() -> impl Future; // <-- RPITIT. +} + +fn needs_sendable_future() +where + // How do we further restrict `T::method()` to be `Send + 'static`? +{ + spawn(T::method()); + //~^ ERROR: `impl Future` is not `Send`! +} +``` + +Specifically, we may not want to restrict the *declaration* of `Foo`, since changing it in the declaration would restrict *all* implementations of `Foo`. + +```rust +trait Foo { + fn method() -> impl Future + Send + 'static; + // ~~~~~~~~~~~~~~~~ + // Not what we want. +} +``` + +So, on stable Rust, we have no way of expressing this restriction when using AFIT or RPITIT. In contrast, we can express this today if we were to use a GAT directly: + +```rust +trait Foo { + type MethodFuture: Future; + fn method() -> Self::MethodFuture; +} + +fn needs_sendable_future() +where + // We can restrict this to only implementors of `Foo` + // whose `MethodFuture` is `Send + 'static`, so we can + // call `spawn` below: + T::MethodFuture: Send + 'static +{ + spawn(T::method()); +} +``` + +However, using GATs means that implementors of `Foo` have to write out the return type explicitly, `type MethodFuture = ...`, which doesn't ([yet](https://github.com/rust-lang/rust/pull/120700)) work if we have an anonymous, unnameable `Future` type! + +## The solution + +In [RFC 3654] we introduced return type notation (RTN). This will allow us to write `where` clause bounds that restrict the return types of functions and methods that use async fn in traits (AFIT) and return-position impl Trait in traits (RPITIT). Extending the example above, RTN lets us write: + +```rust +fn needs_sendable_future() +where + T::method(..): Send + 'static // ✨ +{ + spawn(T::method()); + //~^ Works! +} +``` + +## Restrictions + +Currently, RTN is only allowed for trait associated functions and methods with lifetime generics (not const or type generics) that use: + +* async fn in traits (AFIT) +* return-position impl Trait in traits (RPITIT) where the impl Trait is the outermost return type, i.e. `-> impl Trait`, but not `-> Box` + +These restrictions are described in further detail in [RFC 3654]. + +## How do I help? + +We'd love for you to test out this new feature! + +Specifically, we'd like for you to identify traits where you're unnecessarily restricting your trait definitions with `+ Send` or similar bounds: + +```rust +// Instead of writing a trait like: + +trait Foo { + fn method() -> impl Future + Send + 'static; +} + +// Write this: + +trait Foo { + async fn method(); +} + +// And then at the call site, add: + +fn use_foo() +where + T::method(..): Send + 'static, +{} +``` + +Similarly, we'd like for you to identify traits that currently are returning GATs for the same reason: + +```rust +// Instead of writing this in the trait and call site: + +trait Foo { + type MethodFuture: Future; + fn method() -> Self::MethodFuture; +} + +fn use_foo() +where + T::MethodFuture: Send + 'static, +{} + +// Write this: + +trait Foo { + async fn method(); +} + +fn use_foo() +where + T::method(..): Send + 'static, +{} +``` + +Note, however, that we don't yet support RTN in type position. So while, with the first version, you can write: + +```rust +struct Bar { + field: T::MethodFuture, +} +``` + +You can't yet, with the second version, write: + +```rust +struct Bar { + field: T::method(..), +} +``` + +We'd be interested in hearing about any places where you would run into this limitation. + +We're excited for RTN to make it easier to use async fn in traits (AFIT) in `Send`-bound-heavy async Rust ecosystems. + +As always, take a look at the [RFC][RFC 3654] itself for a detailed explanation for why we settled on this design, in particular the [frequently-asked questions and rationale](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#rationale-and-alternatives). + +[RFC 3654]: https://rust-lang.github.io/rfcs/3654-return-type-notation.html +[RFC 3425]: https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html From c693b59b9469e9a645a5587c8e6c39d2926747e1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 Sep 2024 12:10:38 -0400 Subject: [PATCH 2/5] Make sure to update --- posts/inside-rust/2024-09-23-rtn-call-for-testing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md index 3b67fa47e..8c7c11cdb 100644 --- a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md +++ b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md @@ -87,7 +87,9 @@ These restrictions are described in further detail in [RFC 3654]. ## How do I help? -We'd love for you to test out this new feature! +We'd love for you to test out this feature on the latest Rust nightly compiler[^nightly]. + +[^nightly]: Make sure to run `rustup update nightly` or however manage your Rust releases, since the feature is very new and is still unstable! Specifically, we'd like for you to identify traits where you're unnecessarily restricting your trait definitions with `+ Send` or similar bounds: From c2c59cb02f050e0e7c18bfa69d86c9ed58c0a5a7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 23 Sep 2024 13:18:14 -0400 Subject: [PATCH 3/5] Update posts/inside-rust/2024-09-23-rtn-call-for-testing.md --- posts/inside-rust/2024-09-23-rtn-call-for-testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md index 8c7c11cdb..e5392c759 100644 --- a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md +++ b/posts/inside-rust/2024-09-23-rtn-call-for-testing.md @@ -89,7 +89,7 @@ These restrictions are described in further detail in [RFC 3654]. We'd love for you to test out this feature on the latest Rust nightly compiler[^nightly]. -[^nightly]: Make sure to run `rustup update nightly` or however manage your Rust releases, since the feature is very new and is still unstable! +[^nightly]: Make sure to run `rustup update nightly` or however you manage your Rust releases, since the feature is very new and is still unstable! Specifically, we'd like for you to identify traits where you're unnecessarily restricting your trait definitions with `+ Send` or similar bounds: From a8f65cd5e448406bcde435270be8263b7ee52cbe Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 26 Sep 2024 19:54:27 +0000 Subject: [PATCH 4/5] Update title and date for RTN post --- ...n-call-for-testing.md => 2024-09-26-rtn-call-for-testing.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename posts/inside-rust/{2024-09-23-rtn-call-for-testing.md => 2024-09-26-rtn-call-for-testing.md} (99%) diff --git a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md b/posts/inside-rust/2024-09-26-rtn-call-for-testing.md similarity index 99% rename from posts/inside-rust/2024-09-23-rtn-call-for-testing.md rename to posts/inside-rust/2024-09-26-rtn-call-for-testing.md index e5392c759..67974e7ab 100644 --- a/posts/inside-rust/2024-09-23-rtn-call-for-testing.md +++ b/posts/inside-rust/2024-09-26-rtn-call-for-testing.md @@ -1,6 +1,6 @@ --- layout: post -title: "Return Type Notation MVP: Call for Testing!" +title: "Return type notation MVP: Call for testing!" author: Michael Goulet team: The Async Working Group --- From 2d8cf77a6dfe2636205cff89f362593b398a7c25 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 26 Sep 2024 20:08:35 +0000 Subject: [PATCH 5/5] Make some editorial tweaks on the RTN post These tweaks were informed by looking at the rendered copy. --- .../inside-rust/2024-09-26-rtn-call-for-testing.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/posts/inside-rust/2024-09-26-rtn-call-for-testing.md b/posts/inside-rust/2024-09-26-rtn-call-for-testing.md index 67974e7ab..5a6b5a7c1 100644 --- a/posts/inside-rust/2024-09-26-rtn-call-for-testing.md +++ b/posts/inside-rust/2024-09-26-rtn-call-for-testing.md @@ -5,7 +5,7 @@ author: Michael Goulet team: The Async Working Group --- -The async working group is excited to announce that [RFC 3654] return type notation (RTN) is ready for testing. In this post, we want to briefly describe the feature and announce a call for testing on nightly Rust. +The async working group is excited to announce that [RFC 3654] return type notation (RTN) is ready for testing on nightly Rust. In this post, we'll briefly describe the feature. ## The backstory @@ -24,7 +24,8 @@ trait Foo { fn needs_sendable_future() where - // How do we further restrict `T::method()` to be `Send + 'static`? + // How do we further restrict `T::method()` + // to be `Send + 'static`? { spawn(T::method()); //~^ ERROR: `impl Future` is not `Send`! @@ -69,7 +70,7 @@ In [RFC 3654] we introduced return type notation (RTN). This will allow us to wr ```rust fn needs_sendable_future() where - T::method(..): Send + 'static // ✨ + T::method(..): Send + 'static // Yay! { spawn(T::method()); //~^ Works! @@ -80,8 +81,8 @@ where Currently, RTN is only allowed for trait associated functions and methods with lifetime generics (not const or type generics) that use: -* async fn in traits (AFIT) -* return-position impl Trait in traits (RPITIT) where the impl Trait is the outermost return type, i.e. `-> impl Trait`, but not `-> Box` +* async fn in traits (AFIT) or +* return-position impl Trait in traits (RPITIT) where the impl Trait is the outermost return type, i.e. `-> impl Trait`, but not `-> Box`. These restrictions are described in further detail in [RFC 3654]. @@ -89,7 +90,7 @@ These restrictions are described in further detail in [RFC 3654]. We'd love for you to test out this feature on the latest Rust nightly compiler[^nightly]. -[^nightly]: Make sure to run `rustup update nightly` or however you manage your Rust releases, since the feature is very new and is still unstable! +[^nightly]: Make sure to run `rustup update nightly` (or however you manage your Rust releases), since the feature is very new and is still unstable! Specifically, we'd like for you to identify traits where you're unnecessarily restricting your trait definitions with `+ Send` or similar bounds: