From 460b3aba36a6e610d516a09b5e179009c5f993bf Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 17 Jul 2025 09:22:20 +0300 Subject: [PATCH 1/9] Proposal: Autoreborrow traits --- src/2025h2/autoreborrow-traits.md | 139 ++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/2025h2/autoreborrow-traits.md diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md new file mode 100644 index 00000000..7305f48d --- /dev/null +++ b/src/2025h2/autoreborrow-traits.md @@ -0,0 +1,139 @@ +# Nightly support for Autoreborrow traits + +| Metadata | | +|:-----------------|----------------------------------------------------------------------------------| +| Point of contact | @aapoalas | +| Teams | <!-- TEAMS WITH ASKS --> | +| Task owners | <!-- TASK OWNERS --> | +| Status | Proposed | +| Tracking issue | *if this is a continuing goal, add the old tracking issue, else leave blank* | +| Zulip channel | N/A (an existing stream can be re-used or new streams can be created on request) | + +## Summary + +Bring up a language RFC for autoreborrow traits and land nightly support for the traits. + +## Motivation + +Reborrowing is an important feature of the Rust ecosystem, underpinning much of the borrow checker's work and +enabling ergonomic usage of references. The built-in, automatic handling of reborrows as a language feature +is, arguably, one of the many keys to Rust's success. Autoreborrowing is not available for user-space types +to take advantage of, which hinders their ergonomics and usage. + +Autoreborrowing is a necessary feature for both niche use-cases around lifetime trickery, and for flagship +goals like Pin-ergonomics and the Rust for Linux project. As both of these are proceeding, a parallel track +to pre-empt reborrowing becoming a sticking point seems wise. Luckily, reborrowing is arguably a solved +problem that mostly needs a formal definition and a implementation choice to enter the language formally. + +### The status quo + +Today, when an `Option<&mut T>` or `Pin<&mut T>` is passed as a parameter, it becomes unavailable to the +caller because it gets moved out of. This makes sense insofar as `Option` only being `Copy` if `T: Copy`, +which `&mut T` is not. But it makes no sense from the point of view of `&mut T` specifically: passing an +exclusive reference does not move the reference but instead reborrows it, allowing the `&mut T` to be reused +after the call finishes. Since an `Option<&mut T>` is simply a maybe-null `&mut T` it would make sense that +it would have the same semantics. + +The lack of autoreborrowing is why this is not the case. This can be overcome by using the `.as_deref_mut()` +method but it suffers from lifetime issues when the callee returns a value derived from the `&mut T`: that +returned value cannot be returned again from the caller as its lifetime is bound to the `.as_deref_mut()` +call. For `Pin<&mut T>` this problem has been side-stepped by adding `Pin`-specific nightly support of +reborrowing pinned exclusive references. + +But the same issue pops up again for any user-defined exclusive reference types, such as `Mut<'_, T>`. The +user can define this type as having exclusive semantics by not making the type `Copy`, but they cannot opt +into automatic reborrowing. The best they can hope is to implement a custom `.reborrow_mut()` method similar +to the `Option::as_deref_mut` from above. Here again they run into the issue that the lifetime of a +`Mut<'_, T>` always gets constrained to the closes `.reborrow_mut()` call, making it impossible to return +values derived from a `Mut<'_, T>` from the function that called `.reborrow_mut()`. + +An improvement is needed. + +### The next 6 months + +- Bring up an RFC for autoreborrowing: a + [draft](https://github.com/aapoalas/rfcs/blob/autoreborrow-traits/text/0000-autoreborrow-traits.md) exists. +- Choose the internal logic of recursive reborrowing: based on core marker types, or on interaction with + `Copy`? +- Implement nightly support for non-recursive reborrowing. +- Gather feedback from users, especially `reborrow` crate users. +- Implement nightly support for recursive reborrowing. + +### The "shiny future" we are working towards + +`Pin` ergonomics group should be able to get rid of special-casing of `Pin` reborrowing in rustc. + +Rust for Linux project should be enabled to experiment with custom reborrowable reference types in earnest. + +Users of `reborrow` crate and similar should be enabled to move to core solution. + +## Design axioms + +- Accept the definition of reborrowing as "a memory copy with added lifetime analysis". + - This disallows running user code on reborrow. + - "Reborrow-as-shared" likely needs to run user code; this'd preferably be ignored where possible. +- Must achieve true reborrowing, not a fascimile. + - Current userland reborrowing uses `T::reborrow_mut` functions that achieve the most important part of + reborrowing, temporarily disabled `T` upon reborrow. + - Userland cannot achieve true reborrowing: true reborrowing does not constrain the lifetime of `T`, + whereas userland fascimile does. + - Difference is in whether values derived from a reborrow can be returned past the point of the reborrow. +- Performance of the solution must be trivial: reborrow is checked for at every coercion site. This cannot be + slow. +- Make sure autoreborrowing doesn't become a vehicle for implicit type coercion. Allowing autoreborrowing + from `T` to multiple values could be abused to define a `CustomInt(int128)` that coerces to all integer + types. + - Autoreborrow traits should use an associated type instead of a type parameter. + - Autoreborrowing at coercion sites should not dovetail into eg. an `Into::into` call. + +## Ownership and team asks + +| Task | Owner(s) or team(s) | Notes | +|------------------------------|---------------------|-------| +| Discussion and moral support | ![Team][] [lang] | Normal RFC process | +| Help around impl | ![Team][] [compiler]| Trait-impl querying in rustc to replace `Pin<&mut T>` special case | +| Do the work | @aapoalas | | + +### Design autoreborrow internals + +The basic idea of autoreborrowing is simple enough: when a reborrowable type is encountered at a coercion +site, attempt a reborrow operation. + +Complications arise when reborrowing becomes recursive: if a `struct X { a: A, b: B }` contains two +reborrowable types `A` and `B`, then we'd want the reborrow of `X` to be performed "piecewise". As an +example, the following type should, upon reborrow, only invalidate any values that depend on the `'a` lifetime while any values dependent on the `'b` lifetime should still be usable as normal. + +```rust +struct X<'a, 'b> { + a: &'a mut A, + b: &'b B, +} +``` + +To enable this, reborrowing needs to be defined as a recursive operation but what the "bottom-case" is, that +is the question. One option would be to use `!Copy + Reborrow` fields, another would use core marker types +like `PhantomExclusive<'a>` and `PhantomShared<'b>` to discern the difference. + + +| Task | Owner(s) or team(s) | Notes | +|----------------------|------------------------------------|---------------------------------------------------------------------| +| Lang-team experiment | ![Team][] [lang] | allows coding pre-RFC; only for trusted contributors | +| Author RFC | *Goal point of contact, typically* | | +| Lang-team champion | ![Team][] [lang] | Username here | +| RFC decision | ![Team][] [lang] | | +| Secondary RFC review | ![Team][] [types] | request bandwidth from a second team, most features don't need this | + +### Implement non-recursive autoreborrowing + +A basic autoreborrowing feature should not be too complicated: the `Pin<&mut T>` special-case in the +compiler already exists and could probably be reimagined to rely on a `Reborrow` trait. + +| Task | Owner(s) or team(s) | Notes | +|-----------------------------------|------------------------------------|-------| +| Implementation | *Goal point of contact, typically* | | +| Standard reviews | ![Team][] [compiler] | | +| Lang-team champion | ![Team][] [lang] | | +| Design meeting | ![Team][] [lang] | | +| Author call for testing blog post | *Goal point of contact, typically* | | + +## Frequently asked questions From 7844728840a1f6df0503f71e469d4e786eedf3cd Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 17 Jul 2025 09:37:53 +0300 Subject: [PATCH 2/9] md validity --- src/2025h2/autoreborrow-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index 7305f48d..faeb2dd8 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -2,11 +2,11 @@ | Metadata | | |:-----------------|----------------------------------------------------------------------------------| -| Point of contact | @aapoalas | +| Point of contact | @aapoalas | | Teams | <!-- TEAMS WITH ASKS --> | | Task owners | <!-- TASK OWNERS --> | | Status | Proposed | -| Tracking issue | *if this is a continuing goal, add the old tracking issue, else leave blank* | +| Tracking issue | | | Zulip channel | N/A (an existing stream can be re-used or new streams can be created on request) | ## Summary From 3653c00e00cf747d8417760f1246b7247f0f6af2 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 17 Jul 2025 09:39:11 +0300 Subject: [PATCH 3/9] metadata: Teams --- src/2025h2/autoreborrow-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index faeb2dd8..b947dd60 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -3,7 +3,7 @@ | Metadata | | |:-----------------|----------------------------------------------------------------------------------| | Point of contact | @aapoalas | -| Teams | <!-- TEAMS WITH ASKS --> | +| Teams | <!-- lang, compiler --> | | Task owners | <!-- TASK OWNERS --> | | Status | Proposed | | Tracking issue | | From 0c11c1831135aa7bb54eaf8f62da2a8fafce7f9b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 17 Jul 2025 22:41:38 +0300 Subject: [PATCH 4/9] revert --- src/2025h2/autoreborrow-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index b947dd60..faeb2dd8 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -3,7 +3,7 @@ | Metadata | | |:-----------------|----------------------------------------------------------------------------------| | Point of contact | @aapoalas | -| Teams | <!-- lang, compiler --> | +| Teams | <!-- TEAMS WITH ASKS --> | | Task owners | <!-- TASK OWNERS --> | | Status | Proposed | | Tracking issue | | From 659e2a98a17a34a114c3f7cde7297d7b0b7467f0 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 18 Jul 2025 09:43:02 +0300 Subject: [PATCH 5/9] remove "closes" Co-authored-by: Tyler Mandry --- src/2025h2/autoreborrow-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index faeb2dd8..7cadb9b4 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -44,7 +44,7 @@ But the same issue pops up again for any user-defined exclusive reference types, user can define this type as having exclusive semantics by not making the type `Copy`, but they cannot opt into automatic reborrowing. The best they can hope is to implement a custom `.reborrow_mut()` method similar to the `Option::as_deref_mut` from above. Here again they run into the issue that the lifetime of a -`Mut<'_, T>` always gets constrained to the closes `.reborrow_mut()` call, making it impossible to return +`Mut<'_, T>` always gets constrained to the `.reborrow_mut()` call, making it impossible to return values derived from a `Mut<'_, T>` from the function that called `.reborrow_mut()`. An improvement is needed. From 79f41fbb486f363b55ebe3a77f683e16a1618ddc Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 18 Jul 2025 20:12:46 +0300 Subject: [PATCH 6/9] Update src/2025h2/autoreborrow-traits.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémy Rakic --- src/2025h2/autoreborrow-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index 7cadb9b4..85d5d737 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -91,7 +91,7 @@ Users of `reborrow` crate and similar should be enabled to move to core solution | Task | Owner(s) or team(s) | Notes | |------------------------------|---------------------|-------| | Discussion and moral support | ![Team][] [lang] | Normal RFC process | -| Help around impl | ![Team][] [compiler]| Trait-impl querying in rustc to replace `Pin<&mut T>` special case | +| Standard reviews | ![Team][] [compiler]| Trait-impl querying in rustc to replace `Pin<&mut T>` special case | | Do the work | @aapoalas | | ### Design autoreborrow internals From a177b10fa38a1b49f3039df793ff342e597a59f6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 18 Jul 2025 20:12:59 +0300 Subject: [PATCH 7/9] Update src/2025h2/autoreborrow-traits.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémy Rakic --- src/2025h2/autoreborrow-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index 85d5d737..2139c621 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -121,7 +121,7 @@ like `PhantomExclusive<'a>` and `PhantomShared<'b>` to discern the difference. | Author RFC | *Goal point of contact, typically* | | | Lang-team champion | ![Team][] [lang] | Username here | | RFC decision | ![Team][] [lang] | | -| Secondary RFC review | ![Team][] [types] | request bandwidth from a second team, most features don't need this | +| RFC secondary review | ![Team][] [types] | request bandwidth from a second team, most features don't need this | ### Implement non-recursive autoreborrowing From d271144c4281f89e08ce3b4737c24aa55f931341 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 18 Jul 2025 20:13:11 +0300 Subject: [PATCH 8/9] Update src/2025h2/autoreborrow-traits.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémy Rakic --- src/2025h2/autoreborrow-traits.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index 2139c621..438c5c56 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -4,7 +4,8 @@ |:-----------------|----------------------------------------------------------------------------------| | Point of contact | @aapoalas | | Teams | <!-- TEAMS WITH ASKS --> | -| Task owners | <!-- TASK OWNERS --> | +| Teams | | +| Task owners | | | Status | Proposed | | Tracking issue | | | Zulip channel | N/A (an existing stream can be re-used or new streams can be created on request) | From e70caea879c2cb1352cfedc948a1c75e96e91638 Mon Sep 17 00:00:00 2001 From: Tomas Sedovic Date: Tue, 22 Jul 2025 14:49:28 +0200 Subject: [PATCH 9/9] Fix CI by removing duplicated Teams table with incorrect value --- src/2025h2/autoreborrow-traits.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/2025h2/autoreborrow-traits.md b/src/2025h2/autoreborrow-traits.md index 438c5c56..4cff46d1 100644 --- a/src/2025h2/autoreborrow-traits.md +++ b/src/2025h2/autoreborrow-traits.md @@ -3,7 +3,6 @@ | Metadata | | |:-----------------|----------------------------------------------------------------------------------| | Point of contact | @aapoalas | -| Teams | <!-- TEAMS WITH ASKS --> | | Teams | | | Task owners | | | Status | Proposed |