|
| 1 | +# Nightly support for Autoreborrow traits |
| 2 | + |
| 3 | +| Metadata | | |
| 4 | +|:-----------------|----------------------------------------------------------------------------------| |
| 5 | +| Point of contact | @aapoalas | |
| 6 | +| Teams | <!-- TEAMS WITH ASKS --> | |
| 7 | +| Task owners | <!-- TASK OWNERS --> | |
| 8 | +| Status | Proposed | |
| 9 | +| Tracking issue | | |
| 10 | +| Zulip channel | N/A (an existing stream can be re-used or new streams can be created on request) | |
| 11 | + |
| 12 | +## Summary |
| 13 | + |
| 14 | +Bring up a language RFC for autoreborrow traits and land nightly support for the traits. |
| 15 | + |
| 16 | +## Motivation |
| 17 | + |
| 18 | +Reborrowing is an important feature of the Rust ecosystem, underpinning much of the borrow checker's work and |
| 19 | +enabling ergonomic usage of references. The built-in, automatic handling of reborrows as a language feature |
| 20 | +is, arguably, one of the many keys to Rust's success. Autoreborrowing is not available for user-space types |
| 21 | +to take advantage of, which hinders their ergonomics and usage. |
| 22 | + |
| 23 | +Autoreborrowing is a necessary feature for both niche use-cases around lifetime trickery, and for flagship |
| 24 | +goals like Pin-ergonomics and the Rust for Linux project. As both of these are proceeding, a parallel track |
| 25 | +to pre-empt reborrowing becoming a sticking point seems wise. Luckily, reborrowing is arguably a solved |
| 26 | +problem that mostly needs a formal definition and a implementation choice to enter the language formally. |
| 27 | + |
| 28 | +### The status quo |
| 29 | + |
| 30 | +Today, when an `Option<&mut T>` or `Pin<&mut T>` is passed as a parameter, it becomes unavailable to the |
| 31 | +caller because it gets moved out of. This makes sense insofar as `Option<T>` only being `Copy` if `T: Copy`, |
| 32 | +which `&mut T` is not. But it makes no sense from the point of view of `&mut T` specifically: passing an |
| 33 | +exclusive reference does not move the reference but instead reborrows it, allowing the `&mut T` to be reused |
| 34 | +after the call finishes. Since an `Option<&mut T>` is simply a maybe-null `&mut T` it would make sense that |
| 35 | +it would have the same semantics. |
| 36 | + |
| 37 | +The lack of autoreborrowing is why this is not the case. This can be overcome by using the `.as_deref_mut()` |
| 38 | +method but it suffers from lifetime issues when the callee returns a value derived from the `&mut T`: that |
| 39 | +returned value cannot be returned again from the caller as its lifetime is bound to the `.as_deref_mut()` |
| 40 | +call. For `Pin<&mut T>` this problem has been side-stepped by adding `Pin`-specific nightly support of |
| 41 | +reborrowing pinned exclusive references. |
| 42 | + |
| 43 | +But the same issue pops up again for any user-defined exclusive reference types, such as `Mut<'_, T>`. The |
| 44 | +user can define this type as having exclusive semantics by not making the type `Copy`, but they cannot opt |
| 45 | +into automatic reborrowing. The best they can hope is to implement a custom `.reborrow_mut()` method similar |
| 46 | +to the `Option::as_deref_mut` from above. Here again they run into the issue that the lifetime of a |
| 47 | +`Mut<'_, T>` always gets constrained to the `.reborrow_mut()` call, making it impossible to return |
| 48 | +values derived from a `Mut<'_, T>` from the function that called `.reborrow_mut()`. |
| 49 | + |
| 50 | +An improvement is needed. |
| 51 | + |
| 52 | +### The next 6 months |
| 53 | + |
| 54 | +- Bring up an RFC for autoreborrowing: a |
| 55 | + [draft](https://github.com/aapoalas/rfcs/blob/autoreborrow-traits/text/0000-autoreborrow-traits.md) exists. |
| 56 | +- Choose the internal logic of recursive reborrowing: based on core marker types, or on interaction with |
| 57 | + `Copy`? |
| 58 | +- Implement nightly support for non-recursive reborrowing. |
| 59 | +- Gather feedback from users, especially `reborrow` crate users. |
| 60 | +- Implement nightly support for recursive reborrowing. |
| 61 | + |
| 62 | +### The "shiny future" we are working towards |
| 63 | + |
| 64 | +`Pin` ergonomics group should be able to get rid of special-casing of `Pin` reborrowing in rustc. |
| 65 | + |
| 66 | +Rust for Linux project should be enabled to experiment with custom reborrowable reference types in earnest. |
| 67 | + |
| 68 | +Users of `reborrow` crate and similar should be enabled to move to core solution. |
| 69 | + |
| 70 | +## Design axioms |
| 71 | + |
| 72 | +- Accept the definition of reborrowing as "a memory copy with added lifetime analysis". |
| 73 | + - This disallows running user code on reborrow. |
| 74 | + - "Reborrow-as-shared" likely needs to run user code; this'd preferably be ignored where possible. |
| 75 | +- Must achieve true reborrowing, not a fascimile. |
| 76 | + - Current userland reborrowing uses `T::reborrow_mut` functions that achieve the most important part of |
| 77 | + reborrowing, temporarily disabled `T` upon reborrow. |
| 78 | + - Userland cannot achieve true reborrowing: true reborrowing does not constrain the lifetime of `T`, |
| 79 | + whereas userland fascimile does. |
| 80 | + - Difference is in whether values derived from a reborrow can be returned past the point of the reborrow. |
| 81 | +- Performance of the solution must be trivial: reborrow is checked for at every coercion site. This cannot be |
| 82 | + slow. |
| 83 | +- Make sure autoreborrowing doesn't become a vehicle for implicit type coercion. Allowing autoreborrowing |
| 84 | + from `T` to multiple values could be abused to define a `CustomInt(int128)` that coerces to all integer |
| 85 | + types. |
| 86 | + - Autoreborrow traits should use an associated type instead of a type parameter. |
| 87 | + - Autoreborrowing at coercion sites should not dovetail into eg. an `Into::into` call. |
| 88 | + |
| 89 | +## Ownership and team asks |
| 90 | + |
| 91 | +| Task | Owner(s) or team(s) | Notes | |
| 92 | +|------------------------------|---------------------|-------| |
| 93 | +| Discussion and moral support | ![Team][] [lang] | Normal RFC process | |
| 94 | +| Standard reviews | ![Team][] [compiler]| Trait-impl querying in rustc to replace `Pin<&mut T>` special case | |
| 95 | +| Do the work | @aapoalas | | |
| 96 | + |
| 97 | +### Design autoreborrow internals |
| 98 | + |
| 99 | +The basic idea of autoreborrowing is simple enough: when a reborrowable type is encountered at a coercion |
| 100 | +site, attempt a reborrow operation. |
| 101 | + |
| 102 | +Complications arise when reborrowing becomes recursive: if a `struct X { a: A, b: B }` contains two |
| 103 | +reborrowable types `A` and `B`, then we'd want the reborrow of `X` to be performed "piecewise". As an |
| 104 | +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. |
| 105 | + |
| 106 | +```rust |
| 107 | +struct X<'a, 'b> { |
| 108 | + a: &'a mut A, |
| 109 | + b: &'b B, |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +To enable this, reborrowing needs to be defined as a recursive operation but what the "bottom-case" is, that |
| 114 | +is the question. One option would be to use `!Copy + Reborrow` fields, another would use core marker types |
| 115 | +like `PhantomExclusive<'a>` and `PhantomShared<'b>` to discern the difference. |
| 116 | + |
| 117 | + |
| 118 | +| Task | Owner(s) or team(s) | Notes | |
| 119 | +|----------------------|------------------------------------|---------------------------------------------------------------------| |
| 120 | +| Lang-team experiment | ![Team][] [lang] | allows coding pre-RFC; only for trusted contributors | |
| 121 | +| Author RFC | *Goal point of contact, typically* | | |
| 122 | +| Lang-team champion | ![Team][] [lang] | Username here | |
| 123 | +| RFC decision | ![Team][] [lang] | | |
| 124 | +| RFC secondary review | ![Team][] [types] | request bandwidth from a second team, most features don't need this | |
| 125 | + |
| 126 | +### Implement non-recursive autoreborrowing |
| 127 | + |
| 128 | +A basic autoreborrowing feature should not be too complicated: the `Pin<&mut T>` special-case in the |
| 129 | +compiler already exists and could probably be reimagined to rely on a `Reborrow` trait. |
| 130 | + |
| 131 | +| Task | Owner(s) or team(s) | Notes | |
| 132 | +|-----------------------------------|------------------------------------|-------| |
| 133 | +| Implementation | *Goal point of contact, typically* | | |
| 134 | +| Standard reviews | ![Team][] [compiler] | | |
| 135 | +| Lang-team champion | ![Team][] [lang] | | |
| 136 | +| Design meeting | ![Team][] [lang] | | |
| 137 | +| Author call for testing blog post | *Goal point of contact, typically* | | |
| 138 | + |
| 139 | +## Frequently asked questions |
0 commit comments