From 699da89c99641d4a514e8b325a556a196492740a Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Wed, 8 Sep 2021 18:45:17 -0700 Subject: [PATCH] Move async fundamentals out of this repo Redirect everything to the new repo site. New repo: https://github.com/rust-lang/async-fundamentals-initiative --- src/SUMMARY.md | 7 -- src/design_docs/async_drop.md | 2 +- src/design_docs/async_fn_in_traits.md | 87 ------------- src/design_docs/async_main.md | 6 - src/design_docs/completion_based_futures.md | 4 - src/design_docs/generator_syntax.md | 11 +- src/design_docs/mutex.md | 6 - src/design_docs/stream.md | 47 ------- src/design_docs/yield_safe.md | 27 ---- src/vision/roadmap.md | 4 +- src/vision/roadmap/async_fn.md | 3 +- .../roadmap/async_fn/async_fn_fundamentals.md | 45 ------- .../async_fn_fundamentals/async_closures.md | 40 ------ .../async_fn_fundamentals/async_drop.md | 115 ------------------ .../async_fn_fundamentals/dyn_async_trait.md | 47 ------- .../async_fn_fundamentals/dyn_trait.md | 52 -------- .../async_fn/async_fn_fundamentals/gats.md | 5 - .../impl_trait_in_traits.md | 67 ---------- .../static_async_trait.md | 46 ------- .../async_fn/async_fn_fundamentals/tait.md | 6 - src/vision/roadmap/async_iter/traits.md | 2 +- src/vision/shiny_future/users_manual.md | 22 ++-- src/vision/unresolved_questions/async_fn.md | 2 +- .../unresolved_questions/await_or_not.md | 2 +- 24 files changed, 19 insertions(+), 636 deletions(-) delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/async_closures.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/async_drop.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_trait.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/gats.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/static_async_trait.md delete mode 100644 src/vision/roadmap/async_fn/async_fn_fundamentals/tait.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 5d1b1945..2d3df8cd 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -28,13 +28,6 @@ - [📚 User's manual of the future](./vision/shiny_future/users_manual.md) - [📅 Roadmap](./vision/roadmap.md) - [Async fn everywhere](./vision/roadmap/async_fn.md) - - [Async fn fundamentals](./vision/roadmap/async_fn/async_fn_fundamentals.md) - - [Static async trait](./vision/roadmap/async_fn/async_fn_fundamentals/static_async_trait.md) - - [impl Trait in traits](./vision/roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md) - - [Dyn async trait](./vision/roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md) - - [Dyn trait](./vision/roadmap/async_fn/async_fn_fundamentals/dyn_trait.md) - - [Async drop](./vision/roadmap/async_fn/async_fn_fundamentals/async_drop.md) - - [Async closures](./vision/roadmap/async_fn/async_fn_fundamentals/async_closures.md) - [Boxable async fn](./vision/roadmap/async_fn/boxable.md) - [Async main and tests](./vision/roadmap/async_fn/async_main_and_tests.md) - [Scoped spawn and reliable cancellation](./vision/roadmap/scopes.md) diff --git a/src/design_docs/async_drop.md b/src/design_docs/async_drop.md index e2796fa3..62b5a5b4 100644 --- a/src/design_docs/async_drop.md +++ b/src/design_docs/async_drop.md @@ -1 +1 @@ -# 🗑️ Async drop \ No newline at end of file +# 🗑️ Async drop diff --git a/src/design_docs/async_fn_in_traits.md b/src/design_docs/async_fn_in_traits.md index 4e12312a..92a34e8b 100644 --- a/src/design_docs/async_fn_in_traits.md +++ b/src/design_docs/async_fn_in_traits.md @@ -1,88 +1 @@ # 🧬 Async fn in traits - -* [Why async fn in traits are hard][wafth] - -[wafth]: http://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/ - -## General goal - -```rust,ignore -trait Foo { - // Currently disallowed: - async fn bar(); -} -``` - -## Concerns - -### How to name the resulting future - -If you wanted to name the future that results from calling `bar` (or whatever), you can't. - -Also true for functions `fn bar() -> impl Trait`. - -### Requiring `Send` on futures - -[Relevant thread](https://internals.rust-lang.org/t/how-often-do-you-want-non-send-futures/10360) - -```rust,ignore -async fn foo() {} - -// desugars to -fn foo() -> impl Future { } // resulting type is Send if it can be - -// alternative desugaring we chose not to adopt would require Send -fn foo() -> impl Future + Send { } -``` - -If I want to constrain the future I get back from a method, it is difficult to do without a name: - -```rust,ignore -trait Service { - async fn request(&self); -} - -fn parallel_service() -where - S::Future: Send, -{ - ... -} -``` - -* Should this be solved at the impl trait layer -* Or should we specialize something for async functions -* Would be nice, if there are many, associated types, to have some shorthand - -## Example use case: the Service - -```rust,ignore -trait Service { - type Future: Future; - - fn request(&self, ...) -> Self::Future; -} - -impl Service for MyService { - type Future = impl Future; - - fn request(&self) -> Self::Future { - async move { .. } - } -} -``` - -* Dependent on impl Trait, see lang-team repo - -## Example use case: capturing lifetimes of arguments - -```rust,ignore -trait MyMethod { - async fn foo(&self); -} -``` - -## 🤔 Frequently Asked Questions - -### **What do people say about this to their friends on twitter?** -* (Explain your key points here) diff --git a/src/design_docs/async_main.md b/src/design_docs/async_main.md index fec7dfe8..33767f54 100644 --- a/src/design_docs/async_main.md +++ b/src/design_docs/async_main.md @@ -1,7 +1 @@ # 🎇 Async main - -## What is it? - -## Motivation - -## Frequently Asked Questions \ No newline at end of file diff --git a/src/design_docs/completion_based_futures.md b/src/design_docs/completion_based_futures.md index d1cacd13..028dc22c 100644 --- a/src/design_docs/completion_based_futures.md +++ b/src/design_docs/completion_based_futures.md @@ -1,5 +1 @@ # ⏳ Completion-based futures - -[Notes on io_uring][withoutboats-blog] - -[withoutboats-blog]: https://boats.gitlab.io/blog/post/io-uring diff --git a/src/design_docs/generator_syntax.md b/src/design_docs/generator_syntax.md index ec2acfb0..3eebeebf 100644 --- a/src/design_docs/generator_syntax.md +++ b/src/design_docs/generator_syntax.md @@ -1,10 +1 @@ -# ⚡ Generator syntax - -* It would be useful to be able to write a function to return an iterator or (in the async context) a generator -* The basic shape might be (modulo bikeshedding) `gen fn` that contains `yield` -* Some question marks: - * How general of a mechanism do we want? - * Just target iterators and streams, or shoot for something more general? -* Some of the question marks that arise if you go beyond iterators and streams: - * Return values that are not unit - * Have yield return a value that is passed by the caller of `next` ("resume args") \ No newline at end of file +# ⚡ Generator syntax diff --git a/src/design_docs/mutex.md b/src/design_docs/mutex.md index bcc49dae..78e08305 100644 --- a/src/design_docs/mutex.md +++ b/src/design_docs/mutex.md @@ -1,7 +1 @@ # 🔒 Mutex (future-aware) - -[Description of various challenges with async mutexes][blog] - -[blog]: https://github.com/Diggsey/posts/tree/master/async-mutexes - - diff --git a/src/design_docs/stream.md b/src/design_docs/stream.md index 94878374..4698ca90 100644 --- a/src/design_docs/stream.md +++ b/src/design_docs/stream.md @@ -1,48 +1 @@ # ☔ Stream trait - -* [Current definition](https://docs.rs/futures/0.3/futures/stream/trait.Stream.html) - -## Trait definition - -```rust,ignore -pub trait Stream { - type Item; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll>; - - #[inline] - fn size_hint(&self) -> (usize, Option) { - (0, None) - } -} -``` - -## Concerns - -### Poll-based design - -* You have to think about Pin if you implement this trait. -* Combinators can be more difficult. -* One solution: [generator syntax](./generator_syntax.md). - -### Attached streams are commonly desired - -Sometimes streams need to reuse internal storage ([Discussion]). - -[Discussion]: http://smallcultfollowing.com/babysteps/blog/2019/12/10/async-interview-2-cramertj-part-2/#the-need-for-streaming-streams-and-iterators - -### Combinators - -* Currently the combinations are stored in the [`StreamExt`] module. -* In some cases, this is because of the lack of async closures support. - * Also serves as a "semver barrier". - * Also no-std compatibility. -* One question: what combinators (if any) to include when stabilizing? - * e.g., [`poll_next_unpin`] can make working with pin easier, albeit at a loss of generality - * folks who are new to pinning could use this method, and it can help us to guide the diagnostics by suggesting that they `Box::pin` - -[`StreamExt`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html -[`poll_next_unpin`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.poll_next_unpin diff --git a/src/design_docs/yield_safe.md b/src/design_docs/yield_safe.md index 16b6cab9..ac9c0b1f 100644 --- a/src/design_docs/yield_safe.md +++ b/src/design_docs/yield_safe.md @@ -1,28 +1 @@ # ⚠️ Yield-safe lint - -## Use-case - -Some types should not be held across a "yield" bound. A typical example is a `MutexGuard`: - -```rust,ignore -async fn example(x: &Lock) { - let data = x.lock().unwrap(); - something().await; - *data += 1; -} - -async fn something() { } -``` - -In practice, a lot of these issues are avoided because `MutexGuard` is not `Send`, but single-thread runtimes hit these issues. - -## Types where this would apply - -* `MutexGuard` for mutexes, read-write locks -* Guards for ref-cells -* Things that might use these types internally and wish to bubble it up - -## Precedent and related questions - -* The `#[must_use]` lint on types, we would want their design to work very closely. -* Non-async-friendly functions like `sleep` or `task::block_on`. \ No newline at end of file diff --git a/src/vision/roadmap.md b/src/vision/roadmap.md index bc3d0f63..8e603a9e 100644 --- a/src/vision/roadmap.md +++ b/src/vision/roadmap.md @@ -61,8 +61,8 @@ Clicking on active initiatives also shows a list of *milestones*. These mileston |   ↳ tbd | 💤 | ▰▱▱▱▱▱ | [Async fn everywhere]: ./roadmap/async_fn.md -[fundamentals]: ./roadmap/async_fn/async_fn_fundamentals.md -[Async closures]: ./roadmap/async_fn/async_fn_fundamentals/async_closures.md +[fundamentals]: https://rust-lang.github.io/async-fundamentals-initiative/ +[Async closures]: https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/async_closures.html [Boxable async functions]: ./roadmap/async_fn/boxable.md [Async main and tests]: ./roadmap/async_fn/async_main_and_tests.md [Scoped spawn and reliable cancellation]: ./roadmap/scopes.md diff --git a/src/vision/roadmap/async_fn.md b/src/vision/roadmap/async_fn.md index 175f275b..a5258f45 100644 --- a/src/vision/roadmap/async_fn.md +++ b/src/vision/roadmap/async_fn.md @@ -3,7 +3,6 @@ ## Impact * To a first-order approximation, any place that you can write some sort of Rust function or closure, you should be able to make it asynchronous: - * [in traits and closures, including the Drop trait](./async_fn/async_fn_fundamentals.md) + * [in traits and closures, including the Drop trait](https://rust-lang.github.io/async-fundamentals-initiative/) * in [main and tests](./async_fn/async_main_and_tests.md) * You should be able to [easily create futures that heap allocate their storage](./async_fn/boxable.md), both for performance tuning and for scenarios like recursive functions - diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals.md b/src/vision/roadmap/async_fn/async_fn_fundamentals.md deleted file mode 100644 index 3962e168..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals.md +++ /dev/null @@ -1,45 +0,0 @@ -# Async fn fundamentals - -## Impact - -* Able to write `async fn` in traits and trait impls - * Able to easily declare that `T: Trait + Send` where "every async fn in `Trait` returns a `Send` future" - * Traits that use `async fn` can still be [dyn safe](./async_fn_fundamentals/dyn_async_trait.md) though some tuning may be required - * Async functions in traits desugar to [impl Trait in traits] -* Able to write ["async fn drop"][async drop] to declare that the destructor may await -* Support for [async closures] - -## Milestones - -| Milestone | State | Key participants | -| --- | --- | --- | -| Author [evaluation doc] for [static async trait] | 🦀 | [tmandry] -| Author [evaluation doc] for [dyn async trait] | 🦀 | [tmandry] -| Author [evaluation doc] for [async drop] | 🦀 | [tmandry] -| Author [evaluation doc] for [impl Trait in traits] | 💤 | -| [Stabilize] [type alias impl trait] | 💤 | -| [Stabilize] [generic associated types] | 💤 | -| Author RFC for async fn in traits | 💤 | -| Author [evaluation doc] for [async closures] | 💤 | -| Author RFC for async fn in traits | 💤 | -| [Feature complete] for async fn in traits | 💤 | -| [Feature complete] for [impl Trait in traits] | 💤 | -| [Feature complete] for [async drop] | 💤 | -| [Feature complete] for [async closures] | 💤 | - -[nikomatsakis]: https://github.com/nikomatsakis/ -[oli-obk]: https://github.com/oli-obk/ -[jackh726]: https://github.com/jackh726/ -[tmandry]: https://github.com/tmandry/ -[basics]: ./async_fn_fundamentals/basics.md -[async drop]: ./async_fn_fundamentals.md -[async closures]: ./async_fn_fundamentals/async_closures.md -[impl Trait in traits]: ./async_fn_fundamentals/impl_trait_in_traits.md -[type alias impl trait]: ./async_fn_fundamentals/tait.md -[generic associated types]: ./async_fn_fundamentals/gats.md -[static async trait]: ./async_fn_fundamentals/static_async_trait.md -[dyn async trait]: ./async_fn_fundamentals/dyn_async_trait.md - -[evaluation doc]: ./roadmap/stages.html#evaluation -[stabilize]: https://lang-team.rust-lang.org/initiatives/process/stages/stabilized.html -[feature complete]: https://lang-team.rust-lang.org/initiatives/process/stages/feature_complete.html diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/async_closures.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/async_closures.md deleted file mode 100644 index b6630f17..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/async_closures.md +++ /dev/null @@ -1,40 +0,0 @@ -# Async closures - -## Impact - -* Able to create async closures that work like ordinary closures but which can await values. -* Analogous traits to `Fn`, `FnMut`, `FnOnce`, etc -* Reconcile async blocks and async closures - -## Design notes - -Async functions need their own traits, analogous to `Fn` and friends: - -```rust -#[repr(async_inline)] -trait AsyncFnOnce { - type Output; - - // Uh-oh! You can't encode these as `async fn` using inline async functions! - async fn call(mut self, args: A) -> Self::Output; -} - -#[repr(async_inline)] -trait AsyncFnMut: AsyncFnOnce { - type Output; - - async fn call_mut(&mut self, args: A) -> Self::Output; -} - -#[repr(async_inline)] -trait AsyncFn: AsyncFnMut { - // Uh-oh! You can't encode these as `async fn` using inline async functions! - async fn call(&self, args: A) -> Self::Output; -} -``` - -Some notes: - -- `AsyncFnOnce` is really the same as [`Async`](../compose_control_scheduling/async_trait.md) -- both represent, effectively, a future that can be driven exactly once. -- The concept of `AsyncFn` is more reasonable, but it requires storing the state externally to make sense: how else can there be multiple parallel executions. -- Something is a bit off here. diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/async_drop.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/async_drop.md deleted file mode 100644 index 4c228172..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/async_drop.md +++ /dev/null @@ -1,115 +0,0 @@ -# Async drop - -## Impact - -* Able to create types (database connections etc) that perform async operations on cleanup -* Able to detect when such types are dropped synchronously -* Able to identify the await points that result from async cleanup if needed - -## Requires - -* [inline async fn support](./inline_async_fn.md) - -## Design notes - -We can create a `AsyncDrop` variant that contains an `async fn`: - -```rust -impl AsyncDrop for MyType { - async fn drop(&mut self) { - ... - } -} -``` - -Like `Drop`, the `AsyncDrop` trait must be implemented for all values of its self-type. - -### Async drop glue - -Within async functions, when we drop a value, we will invoke "async drop glue" instead of "drop glue". "Async drop glue" works in the same basic way as "drop glue", except that it invokes `AsyncDrop` where appropriate (and may suspend): - -* The async drop glue for a type T first executes the `AsyncDrop` method - * If `T` has no `AsyncDrop` impl, then the glue executes the synchronous `Drop` impl - * If `T` has no `Drop` impl, then this is a no-op -* The async drop glue then recursively "async drops" all fields of T - -### Requires inline fn - -Making this work requires [inline async fn]. This is because Rust presently assumes *all* types are droppable. Consider a function `foo`: - -```rust -async fn foo(x: T) {} -``` - -Here, we will drop `x` when `foo` returns, but we do not know whether `T` implements `AsyncDrop` or not, and we won't know until monomorphization. However, to know whether the resulting future for `foo(x)` is `Send`, we have to know whether the code that drops `x` will be send. Using an inline function, we know that `T: Send` implies that the async drop future for `T` is `Send`. - -Another argument in favor of [inline async fn] is that dropping ought not to create a lot more memory. - -[inline async fn]: ./inline_async_fn.md - -### Explicit async drop - -We should have a `std::mem::async_drop` analogous to `std::mem::drop`: - -```rust -async fn async_drop(x: T) { } -``` - -### Implicit await points - -When you run async drop glue, there is an implicit await point. Consider this example: - -```rust -async fn foo(dbc: DatabaseConnection) -> io::Result<()> { - let data = socket().read().await?; - dbc.write(data).await?; -} -``` - -Here, presuming that `DatabaseConnection` implements `AsyncDrop`, there are actually a number of async drops occurring: - -```rust -async fn foo(dbc: DatabaseConnection) -> io::Result<()> { - let data = match socket().read().await { - Ok(v) => v, - Err(e) => { - std::mem::async_drop(dbc).await; - return e; - } - }; - let () = match dbc.write(data).await? { - Ok(()) => (), - Err(e) => { - std::mem::async_drop(dbc).await; - return e; - } - }; - std::mem::async_drop(dbc).await; -} -``` - -As this example shows, there are important ergonomic benefits here to implicit async drop, and it also ensures that async and sync code work in analogous ways. However, implicit await points can be a hazard for some applications, where it is important to identify all await points explicitly (for example, authors of embedded applications use await points to reason about what values will be stored in the resulting future vs the stack of the poll function). To further complicate things, async-drop doesn't only execute at the end of a block or an "abrupt" expression like `?`: async-drop can also execute at the end of every statement, given temporary values. - -The best solution here is unclear. We could have an "allow-by-default" lint encouraging explicit use of `async_drop`, but as the code above shows, the result may be highly unergonomic (also, imagine how it looks as the number of variables requiring async-drop grows). - -Another option is to target the problem from another angle, for example by adding lints to identify when large values are stored in a future or on the stack, or to allow developers to tag local variables that they expect to be stored on the stack, and have the compiler warn them if this turns out to not be true. Users could then choose how to resolve the problem (for example, by shortening the lifetime of the value so that it is not live across an await). - -### Preventing sync drop - -It is easy enough to make async-drop be used, but it is currently not possible to prevent sync drop, even from within an async setting. Consider an example such as the following: - -```rust -async fn foo(dbc: DatabaseConnection) -> io::Result<()> { - drop(dbc); -} -``` - -The compiler could however lint against invoking (or defining!) synchronous functions that take ownership of values whose types implement `AsyncDrop`. This would catch code like the case above. We may have to tune the lint to avoid false warnings. Note that it is important to lint *both* invocation *and* definition sites because the synchronous function may be generic (like `drop`, in fact). - -The question remains: what should code that implements `AsyncDrop` *do* if synchronous `Drop` is invoked? One option is panic, but that is suboptimal, as panic from within a destructor is considered bad practice. Another option is to simply abort. A final option is to have some form of portable "block-on" that would work, but this is effectively the (as yet unsolved) [async-sync-async sandwich problem](../../unresolved_questions/async_sync_async_sandwich.md). - -Preventing this 'properly' would require changing fundamental Rust assumptions (e.g., by introducing the `?Drop` trait). While such a change would make Rust more expressive, it also carries complexity and composition hazards, and would require thorough exploration. It is also a step that could be taken later (although it would require some form of explicit `impl !Drop` opt-in by types to avoid semver breakage). - -### Supporting both sync and async drop - -Final point: it should perhaps be possible to support *both* sync and async drop. It is not clear though if there are any real use cases for this. \ No newline at end of file diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md deleted file mode 100644 index f02f8f24..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md +++ /dev/null @@ -1,47 +0,0 @@ -# Dyn async trait - -## Impact - -* Traits that contain [`async fn`](../async_fn_fundamentals.md) or [impl trait in traits](./impl_trait_in_traits.md) can still be dyn safe -* Costs like boxing of futures are limited to code that uses `dyn Trait` and not to all users of the trait -* Reasonable defaults around things like `Send + Sync` and what kind of boxing is used -* Ability to customize those defaults for individual traits or on a crate-wide or module-wide basis - -## Requires - -* [dyn trait](./dyn_trait.md) - -## Design notes - -* Permit a trait `TheTrait` containing `async fn` or [impl trait in traits](./impl_trait_in_traits.md) to be used with `dyn TheTrait`, at least if other criteria are met. -* Do not require annoying annotations. -* Permit the user to select, for `TheTrait`, how the futures will be boxed or otherwise represented, which would permit us to use `Box` or potentially other types like `SmallBox` etc. -* User should also be able to control whether the resulting futures are assumed to be send. - -### Older notes - - -The most basic desugaring of async fn in traits will make the trait not dyn-safe. "Inline" async fn in traits is one way to circumvent that, but it's not suitable for all traits that must be dyn-safe. There are other efficient options: - -- Return a `Box>` -- but then we must decide if it will be `Send`, right? And we'd like to only do that when using the trait as a `dyn Trait`. Plus it is not compatible with no-std (it is compatible with alloc). - - This comes down to needing some form of opt-in. - -This concern applies equally to other "`-> impl Trait` in trait" scenarios. - -We have looked at revising how "dyn traits" are handled more generally in the lang team on a number of occasions, but [this meeting](https://github.com/rust-lang/lang-team/blob/master/design-meeting-minutes/2020-01-13-dyn-trait-and-coherence.md) seems particularly relevant. In that meeting we were discussing some soundness challenges with the existing dyn trait setup and discussing how some of the directions we might go enabled folks to write their _own_ `impl Trait for dyn Trait` impls, thus defining for themselves how the mapping from Trait to dyn Trait. This seems like a key piece of the solution. - -One viable route might be: - -- Traits using `async fn` are not, by default, dyn safe. -- You can declare how you want it to be dyn safe: - - `#[repr(inline)]` - - or `#[derive(dyn_async_boxed)]` or some such - - to take an `#[async_trait]`-style approach - - It would be nice if users can declare their own styles. For example, Matthias247 pointed out that the `Box` used to allocate can be reused in between calls for increased efficiency. -- It would also be nice if there's an easy, decent default -- maybe you don't even _have_ to opt-in to it if you are not in `no_std` land. - -## Frequently asked questions - -### What are the limitations around allocation and no-std code? - -"It's complicated". A lot of no-std code does have an allocator (it depends on alloc), though it may require fallible allocation, or permit allocation of fixed quantities (e.g., only at startup, or so long as it can be known to be O(1)). diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_trait.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_trait.md deleted file mode 100644 index 8b8dd184..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/dyn_trait.md +++ /dev/null @@ -1,52 +0,0 @@ -# Dyn trait - -## Impact - -* Soundness holes relating to `dyn Trait` are closed. -* The semver implication of whether a trait is "dyn or not" are clear. -* More kinds of traits are dyn-safe. -* Easily able to have a "dynamically dispatched core" with helper methods. -* Users are able to the "adaptation" from a statically known type (`T: Trait`) into a `dyn Trait`. - -## Design notes - -### Soundness holes - -FIXME-- list various issues here :) - -### Semver implications - -Today, the compiler automatically determines whether a trait is "dyn-safe". This means that otherwise legal additions to the trait (such as new ) - -### More kinds of traits are dyn-safe - -Currently dyn-safe traits exclude a lot of functionality, such as generic methods. We may be able to lift some of those restrictions. - -### Easily able to have a "dynamically dispatched core" with helper methods - -There is a common pattern with e.g. `Iterator` where there is a dynamically dispatched "core method" (`fn next()`) and then a variety of combinators and helper methods that use `where Self: Sized` to side-step dyn-safety checks. These methods often involve generics. We should make this pattern easier and more obvious, and (ideally) make it work better -- e.g., by having those methods *also* available on `dyn Trait` receivers (which seems fundamentally possible). - -### Adaptation - -In the case of async Rust, given a trait `Foo` that contains `async fn` methods, we wish to be able to have the user write `dyn Foo` without having to specify the values of the associated types that contain the future types for those methods. Consider the fully desugard example: - -```rust -trait Foo { - type Method<..>: Future; - fn method() -> Self::Method<..> -} -``` - -Roughly speaking we wish to be able to supply an impl like - -```rust -impl Foo for dyn Foo { - type Method<..> = Box>; - fn method() -> Self::Method { - // call, via vtable, a shim that will create the `Box` - // (or whichever smart pointer is desired) - } -} -``` - -Ideally, this would be a general capability that users can use to control the adaptation of "known types" to `dyn` types for other traits. diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/gats.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/gats.md deleted file mode 100644 index 3e01d08b..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/gats.md +++ /dev/null @@ -1,5 +0,0 @@ -# Generic associated types - -## Impact - -* Able to use associated types with generic parameters, enabling them to model the return value for methods that are generic over lifetimes or types, as well as variety of other scenarios. \ No newline at end of file diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md deleted file mode 100644 index 3ff63350..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md +++ /dev/null @@ -1,67 +0,0 @@ -# impl Trait in traits - -## Summary - -* Able to write `-> impl Trait` in traits -* Able to write `type Foo<..> = impl Trait` in impls ([type alias impl trait](./tait.md), [generic associated types](./gats.md)) - -## Requires - -* [Type alias impl trait](./tait.md) -* [Generic associated types](./gats.md) - -## Design notes - -Support `-> impl Trait` (existential impl trait) in traits. Core idea is to desugar such thing into a (possibly generic) associated type: - -```rust -trait SomeTrait { - fn foo<(&mut self) -> impl Future + '_; -} - -// becomes something like: -// -// Editor's note: The name of the associated type is under debate; -// it may or may not be something user can name, though they should -// have *some* syntax for referring to it. - -trait SomeTrait { - type Foo<'me>: Future + 'me - where - Self: 'me; - - async fn foo(&mut self) -> Self::Foo<'_>; -} -``` - -We also need to support `-> impl Trait` in impls, in which case the body desugars to a "type alias impl trait": - -```rust -impl SomeTrait for SomeType { - fn foo<(&mut self) -> impl Future + '_ { - - } -} - -// becomes something using "type alias impl Trait", like this: - -trait SomeTrait { - type Foo<'me> = impl Future + 'me - where - Self: 'me; - - fn foo(&mut self) -> Self::Foo<'_> { - ... - } -} -``` - -## Frequently asked questions - -### What is the name of that GAT we introduce? - -- I called it `Bar` here, but that's somewhat arbitrary, perhaps we want to have some generic syntax for naming the method? -- Or for getting the type of the method. -- This problem applies equally to other "`-> impl Trait` in trait" scenarios. -- [Exploration doc](https://hackmd.io/IISsYc0fTGSSm2MiMqby4A) - diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/static_async_trait.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/static_async_trait.md deleted file mode 100644 index 588b6181..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/static_async_trait.md +++ /dev/null @@ -1,46 +0,0 @@ -# Static async fn in traits - -## Impact - -* Able to write `async fn` in traits and impls and use them in statically dispatched contexts -* Able to easily declare that `T: Trait + Send` where "every async fn in `Trait` returns a `Send` future" - -## Design notes - -Support async fn syntax in traits. - -The core idea is that it desugars into [impl trait in traits](./impl_trait_in_traits.md): - -```rust -trait SomeTrait { - async fn foo(&mut self); -} - -// becomes: - -trait SomeTrait { - fn foo<(&mut self) -> impl Future + '_; -} -``` - -Naturally it should also work in an impl: - -```rust -impl SomeTrait for someType { - async fn foo(&mut self); -} -``` - -For async functions in traits to be useful, it is important that traits containing `async fn` be dyn-safe, which introduces a number of challenges that we have to overcome. - -## Frequently asked questions - -### Can users easily bound those GATs with `Send`, maybe even in the trait definition? - -- People are likely to want to say "I want every future produced by this trait to be Send", and right now that is quite tedious. -- We need a way to do this. -- This applies equally to other "`-> impl Trait` in trait" scenarios. - -### What about "dyn" traits? - -- See the sections on "inline" and "dyn" async fn in traits below! \ No newline at end of file diff --git a/src/vision/roadmap/async_fn/async_fn_fundamentals/tait.md b/src/vision/roadmap/async_fn/async_fn_fundamentals/tait.md deleted file mode 100644 index bbfa820b..00000000 --- a/src/vision/roadmap/async_fn/async_fn_fundamentals/tait.md +++ /dev/null @@ -1,6 +0,0 @@ -# Type alias impl trait - -## Impact - -* Able to use "unnameable types" in a variety of positions, such as function return types, struct fields, the value of an associated type, and have the compiler infer its value for you. - * "unnameable types" refers to closures, futures, iterators, and any other type that is either impossible or tedious to write in full. diff --git a/src/vision/roadmap/async_iter/traits.md b/src/vision/roadmap/async_iter/traits.md index 9f15f9a4..408289de 100644 --- a/src/vision/roadmap/async_iter/traits.md +++ b/src/vision/roadmap/async_iter/traits.md @@ -26,7 +26,7 @@ trait AsyncIterator { Note the name change from `Stream` to `AsyncIterator`. -One implication of this change is that pinning is no longer necessary when driving an async iterator. For example, one could now write an async iterator that recursively walks through a set of URLs like so (presuming `std::async_iter::from_fn` and [async closures](../async_fn_everywhere/async_closures)): +One implication of this change is that pinning is no longer necessary when driving an async iterator. For example, one could now write an async iterator that recursively walks through a set of URLs like so (presuming `std::async_iter::from_fn` and [async closures](https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/async_closures.html)): ```rust fn explore(start_url: Url) -> impl AsyncIterator { diff --git a/src/vision/shiny_future/users_manual.md b/src/vision/shiny_future/users_manual.md index d5c00e69..3ecb13be 100644 --- a/src/vision/shiny_future/users_manual.md +++ b/src/vision/shiny_future/users_manual.md @@ -61,7 +61,7 @@ This document is a survey of some of the major aspects of writing async function ### Async iterators -So far, using `async` seems like mostly more work to accomplish the same thing, since you have to add `await` keywords everywhere. But async functions are like synchronous functions with superpowers: they have the ability to easily compose complex schedules of parallel and concurrent workloads. This is particularly true when you start messing around with asynchronous iterators. +So far, using `async` seems like mostly more work to accomplish the same thing, since you have to add `await` keywords everywhere. But async functions are like synchronous functions with superpowers: they have the ability to easily compose complex schedules of parallel and concurrent workloads. This is particularly true when you start messing around with asynchronous iterators. Consider this example. Imagine that you have a bunch of networking requests coming in. For each one, you have to do a bit of lightweight preparation, and then some heavyweight processing. This processing can take up a lot of RAM, and takes a while, so you can only process one request at a time, but you would like to do up to 5 instances of that lightweight preparation in parallel while you wait, so that things are all queued up and ready to go. You want a schedule like this, in other words: @@ -131,7 +131,7 @@ async fn do_work(database: &Database) { // Channel to send prepared items over to the // task that processes them one at a time: let mut (tx, rx) = std::async_sync::mpsc::channel(); - + // Spawn a task to spawn tasks: s.spawn(async move || { let work = do_select(database, FIND_WORK_QUERY)?; @@ -173,7 +173,7 @@ The `AsyncRead` and `AsyncWrite` traits are the most common way to do I/O. They ### Async fns in traits, overview -Async functions work in traits, too ([deliv_async_fn_fundamentals]): +Async functions work in traits, too ([deliv_async_fundamentals]): ```rust trait HttpRequest { @@ -326,7 +326,7 @@ Just as in synchronous Rust, you are advised to keep destructors limited in thei #### Caveat: Synchronous drop -One thing to be aware of when you implement `AsyncDrop` is that, because any Rust value can be dropped at any point, the type system will allow your type to be dropped synchronously as well. We do however have a lint that detects the most common cases and gives you a warning, so this is rare in practice. +One thing to be aware of when you implement `AsyncDrop` is that, because any Rust value can be dropped at any point, the type system will allow your type to be dropped synchronously as well. We do however have a lint that detects the most common cases and gives you a warning, so this is rare in practice. **Note:** If a type that implements `AsyncDrop` *but not `Drop`* is dropped synchronously, the program will abort! @@ -342,15 +342,15 @@ One other thing to be aware of is that async drop will trigger implicit awaits e [deliv_cancellation]: ../roadmap/borrowed_data_and_cancellation.md [deliv_borrowed_data]: ../roadmap/borrowed_data_and_cancellation.md [deliv_async_read_write]: ../../design_docs/async_read_write.md -[deliv_impl_trait_in_trait]: ../roadmap/async_fn/async_fn_fundamentals/impl_trait_in_traits.md -[deliv_gats]: ../roadmap/async_fn/async_fn_fundamentals/gats.md -[deliv_tait]: ../roadmap/async_fn/async_fn_fundamentals/tait.md -[deliv_dyn_async_trait]: ../roadmap/async_fn/async_fn_fundamentals/dyn_async_trait.md -[deliv_dyn_trait]: ../roadmap/async_fn/async_fn_fundamentals/dyn_trait.md +[deliv_impl_trait_in_trait]: https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/impl_trait_in_traits.html +[deliv_gats]: https://github.com/rust-lang/generic-associated-types-initiative +[deliv_tait]: https://github.com/rust-lang/rust/issues/63063 +[deliv_dyn_async_trait]: https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/dyn_async_trait.html +[deliv_dyn_trait]: https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/dyn_trait.html [deliv_scope_api]: ../roadmap/scopes/scope_api.md [deliv_inline_async_fn]: ../roadmap/async_fn/inline_async_fn.md -[deliv_async_drop]: ../roadmap/async_fn/async_fn_fundamentals/async_drop.md -[deliv_async_fn_fundamentals]: ../roadmap/async_fn/asaync_fn_in_traits.md +[deliv_async_drop]: https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/async_drop.html +[deliv_async_fundamentals]: https://rust-lang.github.io/async-fundamentals-initiative/ [deliv_async_overloading]: ../roadmap/async_fn/deliv_async_overloading.md [deliv_tooling]: ../roadmap/tooling.md [deliv_boxable]: ../roadmap/async_fn/boxable.md diff --git a/src/vision/unresolved_questions/async_fn.md b/src/vision/unresolved_questions/async_fn.md index a4352cc6..83a9faaa 100644 --- a/src/vision/unresolved_questions/async_fn.md +++ b/src/vision/unresolved_questions/async_fn.md @@ -1,3 +1,3 @@ # How to represent the AsyncFn traits? -As noted in the [async fn page](../portable_generic_code/function.md), the ["inline async fn"](../async_fn_everywhere/dyn_async_fn.md) technique cannot represent async closures. +As noted in the [async fn page](https://rust-lang.github.io/async-fundamentals-initiative/), the ["inline async fn"](https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/dyn_async_trait.html) technique cannot represent async closures. diff --git a/src/vision/unresolved_questions/await_or_not.md b/src/vision/unresolved_questions/await_or_not.md index aa3f6596..fcfac664 100644 --- a/src/vision/unresolved_questions/await_or_not.md +++ b/src/vision/unresolved_questions/await_or_not.md @@ -11,7 +11,7 @@ Basic idea: Appealing characteristics: - **More analogous to sync code.** In sync code, if you want to defer immediately executing something, you make a closure. Same in async code, but it's an async closure. -- **Consistency around async-drop.** If we adopt an [async drop](../roadmap/async_fn/async_fn_fundamentals/async_drop.md) proposal, that implies that there will be "awaits" that occur as you exit a block (or perhaps from the control-flow of a `break` or `?`). These will not be signaled with a `.await`. So you can no longer rely on _every_ await point being visible with a keyword. +- **Consistency around async-drop.** If we adopt an [async drop](https://rust-lang.github.io/async-fundamentals-initiative/design-discussions/async_drop.html) proposal, that implies that there will be "awaits" that occur as you exit a block (or perhaps from the control-flow of a `break` or `?`). These will not be signaled with a `.await`. So you can no longer rely on _every_ await point being visible with a keyword. - **No confusion around remembering to await.** Right now the compiler has to go to some lengths to offer you messages suggesting you insert `.await`. It'd be nice if you just didn't have to remember. - **Room for optimization.** When you first invoke an async function, it can immediately start executing; it only needs to create a future in the event that it suspends. This may also make closures somewhat smaller. - This could be partially achieved by adding an optional method on the trait that compiles a version of the fn meant to be used when it is _immediately awaited_.