From 27d72626f62e9b3477877cbbdd698972570a466b Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 23 Jan 2026 15:50:47 +0100 Subject: [PATCH 1/8] Initial draft for place traits RFC. --- text/0000-place-traits.md | 307 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 text/0000-place-traits.md diff --git a/text/0000-place-traits.md b/text/0000-place-traits.md new file mode 100644 index 00000000000..3d731f5df27 --- /dev/null +++ b/text/0000-place-traits.md @@ -0,0 +1,307 @@ +- Feature Name: `place_traits` +- Start Date: 2026-01-23 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +## Summary +[summary]: #summary + +This RFC introduces the `Place` and `CreatablePlace` traits. These traits allow for +arbitrary types to implement the special behavior of the `Box` type. In particular, it +allows an arbitrary type to act as an owned place allowing values to be (partially) moved +out and moved back in again, and allows for initialization directly in the space allocated +by the type instead of through a copy via the stack similar to what `Box::new` supports. + +## Motivation +[motivation]: #motivation + +Currently the Box type is uniquely special in the rust ecosystem. It is unique in acting +like an owned variable, and allows a number of special optimizations for direct +instantiation of other types in the storage allocated by it. + +This special status comes with two challenges. First of all, Box gets its special status +by being deeply interwoven with the compiler. This is somewhat problematic as it requires +exactly matched definitions of how the type looks between various parts of the compiler +and the standard library. Furthermore, it makes it harder to generalize the copy-less +initialization of boxes to arbitrary allocators. Moving box over to a place trait would +provide a more pleasant and straightforward interface between the compiler and the box +type. + +Second, it is currently impossible to provide a safe interface for user-defined smart +pointer types which provide the option of moving data in and out of it. There have been +identified a number of places where such functionality could be interesting, such as when +removing values from containers, or when building custom smart pointer types for example +in the context of an implementation of garbage collection. + +The traits presented here would also provide workarounds for the problem of initializing +static variables without first having to construct their value on the stack. This would +solve known problems on embedded platforms, where sometimes people have to use unsafe +workarounds for such initializations. + +## Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +This proposal introduces two new unsafe traits: `Place` and `CreatablePlace`: +```rust +unsafe trait Place: DerefMut { + fn place(&mut self) -> *mut Self::Target +} + +unsafe trait CreatablePlace: Place { + type Args + + unsafe fn new_uninit(args: Self::Args) -> Self; + + // Provided + fn new(args: Self::Args, value: Self::Target) -> Self {/* omitted */} +} +``` + +The `Place` trait essentially allows values of the type to be treated as an already- +existing box. That is, they behave like a variable of the type `Deref::Target`, just +stored in a different location than the stack. This means that values of type +`Deref::Target` can be (partially) moved in and out of dereferences of the type, with the +borrow checker ensuring soundness of the resulting code. As an example, if `Foo` +implements `Place` for type `Bar`, the following would become valid rust code: +```rust +fn baz(mut x: Foo) -> Foo { + let y = *x; + *x = y.destructive_update() + x +} +``` + +The `CreatablePlace` trait then further generalizes the special behavior of `Box::new`. +When implemented, it exposes to the compiler the potential for the optimization of +constructing the value directly in the space allocated for it by the type. For example, +today for an expression like `Box::new([0u8;4*1024*1024])`, the compiler has the option of +optimizing this by creating the 4 Mb large array in the new allocation, instead of first +creating it on the stack and then moving it. Should a type `Foo` implement CreatablePlace +then the same optimization will be possible for `Foo::new(some_args, [0u8;4*1024*1024])`, +as the compiler can now desugare that call to something similar to +```rust +unsafe { + let x = Foo::new_uninit(some_args); + *x = [0u8;4*1024*1024]; +} +``` + +When implementing these traits, it is necessary that the type guarantees the following +properties in order to be sound: +- The pointer returned by `place` should be safe to mutate through, and should be live + for the lifetime of `self`. +- On consecutive calls to `place`, the pointer returned should point to the same value. + Note that it is allowed for the actual location pointed to by the pointer to change so + long as the bytes representing the value have been moved to that new location. +- Drop must not try to drop the value returned by `place`, as this will be handled by the + compiler itself. +- Values of the type for which `place` would return an uninitialized value should never be + explicitly created, except by the new_uninit function. + +There is one oddity in the behavior of types implementing `Place` to be aware of. +Automatically elaborated dereferences of values of such types will always trigger an abort +on panic, instead of unwinding when that is enabled. However, generic types constrained to +only implement Deref or DerefMut but not Place will always unwind on panics during +dereferencing, even if the underlying type also implements Place. + +## Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This proposal introduces two main language items, the traits `Place` and `CreatablePlace`. +We also introduce a number of secondary language items which are used to make +implementation easier and more robust, which we shall define as they come up below. + +A type implementing the trait Place is required to act as a place for borrow checking. In +particular +- The value behind the pointer of `Place::place` shall only be mutatable in safe code + through dereferencing the Place implementing type. +- Unsafe code shall preserve the initialization status of the pointer pointed at by + `Place::place`. +- The drop function of the type shall not drop the value exposed through the `Place` + trait. +- Values of the place type for which the pointer returned by `Place::place` is not + initialized or has been moved out of shall not be able to be created in safe code. + +Dereferences of a type implementing `Place` can therefore be lowered directly to MIR, only +being elaborated in a pass after borrow checking. This allows the borrow checker to fully +check that the moves of data into and out of the type are valid. + +The dereferences and drops of the contained value can then be elaborated in the passes +after borrow checking. This process will be somewhat similar to what is already done for +Box, with the difference that dereferences of types implementing `Place` may panic. We +propose to handle these panics by aborting to avoid introducing interactions with drop +elaboration and new execution paths not checked by the borrow checker. + +In order to generate the function calls to the `Place::place` and `Deref::deref` during +the dereference elaboration we propose making these functions additional language items. + +For the implementation of `CreatablePlace` we propose the introduction of a compiler +intrinsic `emplace`, similar to `box_new`. This intrinsic can then expand during MIR +lowering in the same way as box_new, but replacing the call to the allocator with a call +to `CreatablePlace::new_uninit`. A similar intrinsic to `ShallowInitBox` can then be used +to mark the resulting value properly for the borrow checker. + +The `emplace` compiler intrinsic can then be used to provide the implementation for +`CreatablePlace::new`, similar to how `box_new` is used to implement the special behavior +of `Box::new`. + +## Drawbacks +[drawbacks]: #drawbacks + +There are three main drawbacks to the design as outlined above. First, the traits are +unsafe and come with quite an extensive list of requirements on the implementing type. +This makes them relatively tricky and risky to implement, as breaking the requirements +could result in undefined behavior that is difficult to find. + +Second, with the current design the underlying type is no longer aware of whether or not +the space it has allocated for the value is populated or not. This inhibits functionality +which would use this information on drop to automate removal from a container. Note +however that such usecases can use a workaround with the user explicitly requesting +removal before being able to move out of a smart-pointer like type. + +Finally, the type does not have runtime-awareness of when the value is exactly added. +This means that the proposed traits are not suitable for providing transparent locking of +shared variables to end user code. + +In past proposals for similar traits it has also been noted that AutoDeref is complicated +and poorly understood by most users. It could therefore be considered problematic that +AutoDeref behavior is extended. However the behavior here is identical to what Box already +has, which is considered acceptable in its current state. + +## Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Ideas for something like the `Place` trait design here can be found in past discussions of +DerefMove traits and move references. The desire for some way of doing move derefences +goes back to at least https://github.com/rust-lang/rfcs/issues/997. + +The rationale behind the current design is that it explicitly sticks very closely to what +is already implemented for Boxes, which in turn closely mirror what can be done with stack +variables directly. This provides a relatively straightforward mental model for the user, +and significantly reduces the risk that the proposed design runs into issues in the +implementation phase. + +### DerefMove trait + +Designs based on a simpler DerefMove trait have been previously proposed in the unmerged +[RFC2439](https://github.com/rust-lang/rfcs/pull/2439) and an [internals forum thread](https://internals.rust-lang.org/t/derefmove-without-move-why-dont-we-have-it/19701). +These come down to a trait of the form +``` +trait DerefMove : DerefMut { + fn deref_move(self) -> Self::Target +} +``` + +The disadvantage of an approach like this is that it is somewhat unclear how to deal with +partial moves. This has in the past stopped such proposals in their tracks. + +Furthermore, such a trait does not by itself cover the entirety of the functionality +offered by Box, and given its consuming nature it is unclear how to extend it. This also +leads to the potential for backwards incompatible changes to the current behavior of Box, +as has previously been identified. + +### &move based solutions + +A separate class of solutions has been proposed based on the idea of adding &move +references to the type system, where the reference owns the value, but not the allocation +behind the value. These were discussed in an [unsubmitted RFC by arielb1](https://github.com/arielb1/rfcs/blob/missing-derefs/text/0000-missing-derefs.md), +and an [internals forum thread](https://internals.rust-lang.org/t/pre-rfc-move-references/14511) + +Drawbacks of this approach have been indicated as the significant extra complexity which +is added to the type system with the extra type of reference. There further seems to be a +need for subtypes of move references to ensure values are moved in or out before dropping +to properly keep the allocation initialized or deinitialized after use as needed. + +This additional complexity leads to a lot more moving parts in this approach, which +although the result has the potential to allow a bit more flexibility makes them less +attractive on the whole. + +### More complicated place traits + +Several more complicated `Place` traits have been proposed by tema2 in two threads on the +internals forum: +- [DerefMove without `&move` refs](https://internals.rust-lang.org/t/derefmove-without-move-refs/17575) +- [`DerefMove` as two separate traits](https://internals.rust-lang.org/t/derefmove-as-two-separate-traits/16031) + +These traits aimed at providing more feedback with regards to the length of use of the +pointer returned by the `Place::place` method, and the status of the value in that +location after use. Such a design would open up more possible use cases, but at the cost +of significantly more complicated desugarings. + +Furthermore, allowing actions based on whether a value is present or not in the place +would add additional complexity in understanding the control flow of the resulting binary. +This could make understanding uses of these traits significantly more difficult for end +users of types implement these traits. + +### Limited macro based trait + +Going the other way in terms of complexity, a `Place` trait with constraints on how the +projection to the actual location to be dereferenced was proposed in [another internals forum thread](https://internals.rust-lang.org/t/derefmove-without-move-references-aka-box-magic-for-user-types/19910). + +This proposal effectively constrains the `Place::deref` method to only doing field +projections and other dereferences. The advantage of this is that such a trait has far +less severe safety implications, and by its nature cannot panic making its use more +predictable. + +However, the restrictions require additional custom syntax for specifying the precise +process, which adds complexity to the language and makes the trait a bit of an outlier +compared to the `Deref` and `DerefMut` traits. + +### Existing library based solutions + +The [moveit library](https://docs.rs/moveit/latest/moveit/index.html) provides similar +functionality in its `DerefMove` and `Emplace` traits. However, these require unsafe code +on the part of the end user of the traits, which makes them unattractive for developers +wanting the memory safety guarantees rust provides. + +## Prior art +[prior-art]: #prior-art + +The behavior enabled by the traits proposed here is already implemented for the Box type, +which can be considered prime prior art. Experience with the Box type has shown that its +special behaviors have applications. + +Beyond rust, there aren't really comparable features that map directly onto the proposal +here. C++ has the option for smart pointers through overloading dereferencing operators, +and implements move semantics through Move constructors and Move assignment operators. +However, moves in C++ require the moved-out of place to always remain containing a valid +value as there is no intrinsic language-level way of dealing with moved-out of places in +a special way. + +In terms of implementability, a small experiment has been done implementing the deref +elaboration for a joined-together version of these traits at +https://github.com/davidv1992/rust/tree/place-experiment. That implementation is +sufficiently far along to support running code using the Place trait, but does not yet +properly drop the internal value, instead leaking it. + +## Unresolved questions +[unresolved-questions]: #unresolved-questions + +The current design would require the use of `Deref::deref` in desugaring non-moving +accesses to types implementing `Place`. However, it is currently unclear whether it is +sound to do so if the value has already been partially moved out. + +Right now, the design states that panic in calls to `Deref::deref` or `Place::place` can +cause an abort when the call was generated in the MIR. This is done as it is at this point +somewhat unclear how to handle proper unwinding at the call sites for these functions. +However, it may turn out to be possible to implement this with proper unwinding, in which +case we may want to consider handling panics at these call sites the same as for ordinary +code. + +## Future possibilities +[future-possibilities]: #future-possibilities + +Should the trait become stabilized, it may become interesting to implement non-copying +variants of the various push and pop functions on containers within the standard library. +Such functions could allow significant optimizations when used in combination with large +elements in the container. + +It may also be interesting at a future point to reconsider whether the unsized_fn_params +trait should remain internal, in particular once Unsized coercions become usable with user +defined types. However, this decision can be delayed to a later date as sufficiently many +interesting use cases are already available without it. + +There currently is sustained interest in having good solutions for dealing with values in +place from the rust-for-linux project. Although this is not a complete solution to those +problems, the traits proposed here may form part of the solution to in-place creation of +values for various types that need this. From 30fe1f9422c751bae8b011940cbde1a92712ad0c Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Feb 2026 15:22:33 +0100 Subject: [PATCH 2/8] Removed CreatablePlace as its not yet mature enough. This also reworks some of the other sections to further improve the clarity of the message. --- text/0000-place-traits.md | 116 +++++++++++--------------------------- 1 file changed, 33 insertions(+), 83 deletions(-) diff --git a/text/0000-place-traits.md b/text/0000-place-traits.md index 3d731f5df27..5df2354d762 100644 --- a/text/0000-place-traits.md +++ b/text/0000-place-traits.md @@ -6,11 +6,10 @@ ## Summary [summary]: #summary -This RFC introduces the `Place` and `CreatablePlace` traits. These traits allow for -arbitrary types to implement the special behavior of the `Box` type. In particular, it -allows an arbitrary type to act as an owned place allowing values to be (partially) moved -out and moved back in again, and allows for initialization directly in the space allocated -by the type instead of through a copy via the stack similar to what `Box::new` supports. +This RFC introduces the `Place` trait. This trait allows arbitrary types to implement the +special derefence behavior of the `Box` type. In particular, it allows an arbitrary type +to act as an owned place allowing values to be (partially) moved out and moved back in +again. ## Motivation [motivation]: #motivation @@ -22,10 +21,9 @@ instantiation of other types in the storage allocated by it. This special status comes with two challenges. First of all, Box gets its special status by being deeply interwoven with the compiler. This is somewhat problematic as it requires exactly matched definitions of how the type looks between various parts of the compiler -and the standard library. Furthermore, it makes it harder to generalize the copy-less -initialization of boxes to arbitrary allocators. Moving box over to a place trait would -provide a more pleasant and straightforward interface between the compiler and the box -type. +and the standard library. Moving box over to a place trait would provide a more pleasant +and straightforward interface between the compiler and the box type, at least in regards +to the move behavior. Second, it is currently impossible to provide a safe interface for user-defined smart pointer types which provide the option of moving data in and out of it. There have been @@ -33,28 +31,14 @@ identified a number of places where such functionality could be interesting, suc removing values from containers, or when building custom smart pointer types for example in the context of an implementation of garbage collection. -The traits presented here would also provide workarounds for the problem of initializing -static variables without first having to construct their value on the stack. This would -solve known problems on embedded platforms, where sometimes people have to use unsafe -workarounds for such initializations. - ## Guide-level explanation [guide-level-explanation]: #guide-level-explanation -This proposal introduces two new unsafe traits: `Place` and `CreatablePlace`: +This proposal introduces a new unsafe trait `Place`: ```rust unsafe trait Place: DerefMut { fn place(&mut self) -> *mut Self::Target } - -unsafe trait CreatablePlace: Place { - type Args - - unsafe fn new_uninit(args: Self::Args) -> Self; - - // Provided - fn new(args: Self::Args, value: Self::Target) -> Self {/* omitted */} -} ``` The `Place` trait essentially allows values of the type to be treated as an already- @@ -71,32 +55,14 @@ fn baz(mut x: Foo) -> Foo { } ``` -The `CreatablePlace` trait then further generalizes the special behavior of `Box::new`. -When implemented, it exposes to the compiler the potential for the optimization of -constructing the value directly in the space allocated for it by the type. For example, -today for an expression like `Box::new([0u8;4*1024*1024])`, the compiler has the option of -optimizing this by creating the 4 Mb large array in the new allocation, instead of first -creating it on the stack and then moving it. Should a type `Foo` implement CreatablePlace -then the same optimization will be possible for `Foo::new(some_args, [0u8;4*1024*1024])`, -as the compiler can now desugare that call to something similar to -```rust -unsafe { - let x = Foo::new_uninit(some_args); - *x = [0u8;4*1024*1024]; -} -``` +When implementing this trait, the type itself effectively transfers some of the responsibilities for managing the value behind the pointer returned by `Place::place`, also called the content, to the compiler. In particular, the type itself should no longer count on the ccontent being properly initialized and dropable when its `Drop` implementation or `Place::place` implementation is called. However, the compiler still guarantees that, as long as the type implementing the place is always created with a value in it, and that value is never removed through a different mechanism than dereferencing the type, all other calls to member functions can assume the value to be implemented. -When implementing these traits, it is necessary that the type guarantees the following -properties in order to be sound: +In general, the compilers requirements are met when - The pointer returned by `place` should be safe to mutate through, and should be live - for the lifetime of `self`. -- On consecutive calls to `place`, the pointer returned should point to the same value. - Note that it is allowed for the actual location pointed to by the pointer to change so - long as the bytes representing the value have been moved to that new location. -- Drop must not try to drop the value returned by `place`, as this will be handled by the - compiler itself. -- Values of the type for which `place` would return an uninitialized value should never be - explicitly created, except by the new_uninit function. + for the lifetime of the mutable reference to `self` passed to `Place::place`. +- On consecutive calls to `Place::place`, the status of whether the content is initialized should not be changed. +- Drop must not drop the contents, only the storage for it. +- Newly initialized values of the type implementing `Place` must have their content initialized. There is one oddity in the behavior of types implementing `Place` to be aware of. Automatically elaborated dereferences of values of such types will always trigger an abort @@ -107,20 +73,13 @@ dereferencing, even if the underlying type also implements Place. ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This proposal introduces two main language items, the traits `Place` and `CreatablePlace`. -We also introduce a number of secondary language items which are used to make -implementation easier and more robust, which we shall define as they come up below. - -A type implementing the trait Place is required to act as a place for borrow checking. In -particular -- The value behind the pointer of `Place::place` shall only be mutatable in safe code - through dereferencing the Place implementing type. -- Unsafe code shall preserve the initialization status of the pointer pointed at by - `Place::place`. -- The drop function of the type shall not drop the value exposed through the `Place` - trait. -- Values of the place type for which the pointer returned by `Place::place` is not - initialized or has been moved out of shall not be able to be created in safe code. +This proposal introduces one new main language item, the traits `Place`. We also introduce a number of secondary language items which are used to make implementation easier and more robust, which we shall define as they come up below. + +A type implementing the trait Place is required to act as a place for borrow checking. Throughout the rest of this text, the contents of the memory pointed at by the pointer returned by the `Place::place` function shall be refered to as the content of the place. For a type to satisfy the above requirement, its implementation must in particular guarantee that +- Safe code shall not modify the initialization status of the contents. +- Unsafe code shall preserve the initialization status of the contents between two derefences of teh type's values. +- Values of the place type for which the content is uninitialized shall not be able to be created in safe code. +In the above context, the contents is also considered uniitialized if the whole or parts of the value of the contents has been moved out, or a destructor has been called upon them. Dereferences of a type implementing `Place` can therefore be lowered directly to MIR, only being elaborated in a pass after borrow checking. This allows the borrow checker to fully @@ -135,16 +94,6 @@ elaboration and new execution paths not checked by the borrow checker. In order to generate the function calls to the `Place::place` and `Deref::deref` during the dereference elaboration we propose making these functions additional language items. -For the implementation of `CreatablePlace` we propose the introduction of a compiler -intrinsic `emplace`, similar to `box_new`. This intrinsic can then expand during MIR -lowering in the same way as box_new, but replacing the call to the allocator with a call -to `CreatablePlace::new_uninit`. A similar intrinsic to `ShallowInitBox` can then be used -to mark the resulting value properly for the borrow checker. - -The `emplace` compiler intrinsic can then be used to provide the implementation for -`CreatablePlace::new`, similar to how `box_new` is used to implement the special behavior -of `Box::new`. - ## Drawbacks [drawbacks]: #drawbacks @@ -250,14 +199,14 @@ compared to the `Deref` and `DerefMut` traits. ### Existing library based solutions The [moveit library](https://docs.rs/moveit/latest/moveit/index.html) provides similar -functionality in its `DerefMove` and `Emplace` traits. However, these require unsafe code -on the part of the end user of the traits, which makes them unattractive for developers -wanting the memory safety guarantees rust provides. +functionality in its `DerefMove` trait. However, this requires unsafe code on the part of +the end user of the trait, which makes them unattractive for developers wanting the +memory safety guarantees rust provides. ## Prior art [prior-art]: #prior-art -The behavior enabled by the traits proposed here is already implemented for the Box type, +The behavior enabled by the trait proposed here is already implemented for the Box type, which can be considered prime prior art. Experience with the Box type has shown that its special behaviors have applications. @@ -269,7 +218,7 @@ value as there is no intrinsic language-level way of dealing with moved-out of p a special way. In terms of implementability, a small experiment has been done implementing the deref -elaboration for a joined-together version of these traits at +elaboration for an earlier version of this trait at https://github.com/davidv1992/rust/tree/place-experiment. That implementation is sufficiently far along to support running code using the Place trait, but does not yet properly drop the internal value, instead leaking it. @@ -292,8 +241,8 @@ code. [future-possibilities]: #future-possibilities Should the trait become stabilized, it may become interesting to implement non-copying -variants of the various push and pop functions on containers within the standard library. -Such functions could allow significant optimizations when used in combination with large +variants of the various pop functions on containers within the standard library. Such +functions could allow significant optimizations when used in combination with large elements in the container. It may also be interesting at a future point to reconsider whether the unsized_fn_params @@ -301,7 +250,8 @@ trait should remain internal, in particular once Unsized coercions become usable defined types. However, this decision can be delayed to a later date as sufficiently many interesting use cases are already available without it. -There currently is sustained interest in having good solutions for dealing with values in -place from the rust-for-linux project. Although this is not a complete solution to those -problems, the traits proposed here may form part of the solution to in-place creation of -values for various types that need this. +Finally, there is potential for the trait as presented here to become useful in the in +place initialization project. It could be a building block for generalizing things like +partial initialization to smart pointers. This would require future design around an api +for telling the borrow checker about new empty values implementing Place, but that seems +orthogonal to the design here. From 7c1ce8d462b591be703bf88872ed07c4d5221b7e Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Wed, 18 Feb 2026 22:38:02 +0100 Subject: [PATCH 3/8] Integrate PR number. --- text/{0000-place-traits.md => 3921-place-traits.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename text/{0000-place-traits.md => 3921-place-traits.md} (99%) diff --git a/text/0000-place-traits.md b/text/3921-place-traits.md similarity index 99% rename from text/0000-place-traits.md rename to text/3921-place-traits.md index 5df2354d762..8a5173467b1 100644 --- a/text/0000-place-traits.md +++ b/text/3921-place-traits.md @@ -1,6 +1,6 @@ - Feature Name: `place_traits` - Start Date: 2026-01-23 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/3921) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) ## Summary From 4dae0fe5ce468b892afd07bc5415a2083e020292 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Mar 2026 11:01:37 +0100 Subject: [PATCH 4/8] Resolved the open question around the need for a non-mut place function. --- text/3921-place-traits.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/text/3921-place-traits.md b/text/3921-place-traits.md index 8a5173467b1..29d8bc2dddb 100644 --- a/text/3921-place-traits.md +++ b/text/3921-place-traits.md @@ -37,7 +37,8 @@ in the context of an implementation of garbage collection. This proposal introduces a new unsafe trait `Place`: ```rust unsafe trait Place: DerefMut { - fn place(&mut self) -> *mut Self::Target + fn place(&self) -> *const Self::Target; + fn place_mut(&mut self) -> *mut Self::Target; } ``` @@ -226,11 +227,7 @@ properly drop the internal value, instead leaking it. ## Unresolved questions [unresolved-questions]: #unresolved-questions -The current design would require the use of `Deref::deref` in desugaring non-moving -accesses to types implementing `Place`. However, it is currently unclear whether it is -sound to do so if the value has already been partially moved out. - -Right now, the design states that panic in calls to `Deref::deref` or `Place::place` can +Right now, the design states that panic in calls to `Place::place` or `Place::place_mut` can cause an abort when the call was generated in the MIR. This is done as it is at this point somewhat unclear how to handle proper unwinding at the call sites for these functions. However, it may turn out to be possible to implement this with proper unwinding, in which From 04d2806f76acc35a97550d83b45af81a58adf002 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Mar 2026 12:26:40 +0100 Subject: [PATCH 5/8] Reworked the formulation of safety requirements. During this process I also sharpened the thinking on abort behavior, resulting in some changes to that unresolved question as well. --- text/3921-place-traits.md | 114 +++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/text/3921-place-traits.md b/text/3921-place-traits.md index 29d8bc2dddb..9b56de048ea 100644 --- a/text/3921-place-traits.md +++ b/text/3921-place-traits.md @@ -42,12 +42,12 @@ unsafe trait Place: DerefMut { } ``` -The `Place` trait essentially allows values of the type to be treated as an already- -existing box. That is, they behave like a variable of the type `Deref::Target`, just -stored in a different location than the stack. This means that values of type -`Deref::Target` can be (partially) moved in and out of dereferences of the type, with the -borrow checker ensuring soundness of the resulting code. As an example, if `Foo` -implements `Place` for type `Bar`, the following would become valid rust code: +The `Place` trait allows values of the type to be treated as a `Box` with a content. That +is, they behave like a variable of type `Deref::Target`, just (potentially) stored in a +different location than the stack. The contents can be (partially) moved in and out of +dereferences of the type, with the borrow checker ensuring soundness of the resulting +code. As an example, if `Foo` implements `Place` for type `Bar`, the following would be +valid rust code: ```rust fn baz(mut x: Foo) -> Foo { let y = *x; @@ -56,14 +56,18 @@ fn baz(mut x: Foo) -> Foo { } ``` -When implementing this trait, the type itself effectively transfers some of the responsibilities for managing the value behind the pointer returned by `Place::place`, also called the content, to the compiler. In particular, the type itself should no longer count on the ccontent being properly initialized and dropable when its `Drop` implementation or `Place::place` implementation is called. However, the compiler still guarantees that, as long as the type implementing the place is always created with a value in it, and that value is never removed through a different mechanism than dereferencing the type, all other calls to member functions can assume the value to be implemented. +In implementing the `Place` trait, the type transfers responsibility for managing its +content to the compiler. In particular, the type should not assume the contents are +initialized when `Place::place` and `Place::place_mut` are called. -In general, the compilers requirements are met when -- The pointer returned by `place` should be safe to mutate through, and should be live - for the lifetime of the mutable reference to `self` passed to `Place::place`. -- On consecutive calls to `Place::place`, the status of whether the content is initialized should not be changed. -- Drop must not drop the contents, only the storage for it. -- Newly initialized values of the type implementing `Place` must have their content initialized. +It also transfers responsibiltiy for dropping the contents to the compiler. This will +be done in drop glue, and the `Drop::drop` function will therefore see the contents as +uninitialized. + +The transfer of responsibility also puts requirements on the implementer of `Place`. In +particular, instances of the type where the contents are uninitialized through a means +other than moving out of a dereference should not be created. Breaking this rule and +dereferencing the resulting instance is immediate undefined behavior. There is one oddity in the behavior of types implementing `Place` to be aware of. Automatically elaborated dereferences of values of such types will always trigger an abort @@ -74,26 +78,57 @@ dereferencing, even if the underlying type also implements Place. ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This proposal introduces one new main language item, the traits `Place`. We also introduce a number of secondary language items which are used to make implementation easier and more robust, which we shall define as they come up below. - -A type implementing the trait Place is required to act as a place for borrow checking. Throughout the rest of this text, the contents of the memory pointed at by the pointer returned by the `Place::place` function shall be refered to as the content of the place. For a type to satisfy the above requirement, its implementation must in particular guarantee that -- Safe code shall not modify the initialization status of the contents. -- Unsafe code shall preserve the initialization status of the contents between two derefences of teh type's values. -- Values of the place type for which the content is uninitialized shall not be able to be created in safe code. -In the above context, the contents is also considered uniitialized if the whole or parts of the value of the contents has been moved out, or a destructor has been called upon them. - -Dereferences of a type implementing `Place` can therefore be lowered directly to MIR, only -being elaborated in a pass after borrow checking. This allows the borrow checker to fully -check that the moves of data into and out of the type are valid. - -The dereferences and drops of the contained value can then be elaborated in the passes -after borrow checking. This process will be somewhat similar to what is already done for -Box, with the difference that dereferences of types implementing `Place` may panic. We -propose to handle these panics by aborting to avoid introducing interactions with drop -elaboration and new execution paths not checked by the borrow checker. - -In order to generate the function calls to the `Place::place` and `Deref::deref` during -the dereference elaboration we propose making these functions additional language items. +This proposal introduces one new main language item, the traits `Place`. We also introduce +a number of secondary language items which are used to make implementation easier and more +robust, which we shall define as they come up below. + +Instances of a type implementing the trait Place shall provide a "contents" of type +`Deref::Target`, which shall act as a place for borrow checking. The implementer of the +`Place` trait shall ensure that: +- The `Place::place` and `Place::place_mut` return pointers to the storage of the content + valid for the lifetime of the self reference. +- The `Place::place` and `Place::place_mut` functions shall not assume the contents to + be initialized +- The `Drop::drop` function shall not interact with the content, but only with its storage. +- With the exception of uninitialization through moving out of a dereference of the instance, + the implementer shall ensure that the contents are always initialized when dereferencing + or dropping instances of the type. + +In return, the compiler shall guarantee that: +- When the contents are uninitialized through moving out of a dereference of an instance, + references to that instance will only be available through invocations of `Place::place`, + `Place::place_mut` and `Drop::drop` by the compiler itself. +- The contents shall be dropped or moved out of by the compiler before invoking `Drop::drop` + of the instance. + +Furthermore, as there seems to be no good reasons for `Place::place` and `Place::place_mut` +to panic, the compiler shall handle panics in non-explicit calls to these functions by +aborting. This allows the compiler to compile code more efficiently and provides more +avenues for optimizations. + +Note that the above requirements explicitly allow changes in the storage location of the +contents. This is only restricted by the contract imposed by `Place` during the lifetime +of self pointers passed to `Place::place` and `Place::place_mut`. + +As a consequence, `Pin` does not automatically satisfy all the requirements of Pin +when Foo implements place. This will need to be verified on an implementation by +implementation basis. + +### Implementation details + +Given the above contract, dereferences of a type implementing `Place` can be lowered +directly to MIR, only being elaborated after borrow checking. This allows the borrow +checker and drop elaboration logic to provide the guarantees above, and ensure that +dereferences are sound in the pressence of moves out of the contents. + +The dereferences and drops of the contained value can be elaborated in the passes after +borrow checking. This process will be somewhat similar to what is already done for Box, +with the difference that dereferences of types implementing `Place` may panic. These +panics will be handled by aborting, avoiding significant changes in the control flow graph. + +In order to generate the function calls to the `Place::place` and `Place::place_mut` +during the dereference elaboration we propose making these functions additional language +items. ## Drawbacks [drawbacks]: #drawbacks @@ -227,12 +262,13 @@ properly drop the internal value, instead leaking it. ## Unresolved questions [unresolved-questions]: #unresolved-questions -Right now, the design states that panic in calls to `Place::place` or `Place::place_mut` can -cause an abort when the call was generated in the MIR. This is done as it is at this point -somewhat unclear how to handle proper unwinding at the call sites for these functions. -However, it may turn out to be possible to implement this with proper unwinding, in which -case we may want to consider handling panics at these call sites the same as for ordinary -code. +Right now, the design states that panic in calls to `Place::place` or `Place::place_mut` +can cause an abort when the call was generated in the MIR. This is done to make compilation +and the resulting code more performant. It is however possible to implement these with +proper unwinding, at the cost of generating a more complicated control flow graph before +borrow checking for code using the dereference behavior of `Place`. This would likely +result in longer compile times and less optimized results, however that could be judged +to be a worthwhile tradeoff. ## Future possibilities [future-possibilities]: #future-possibilities From 32f5d4c5cc80bb86d6e2858dd414fc06d4d7de7d Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Mar 2026 14:02:42 +0100 Subject: [PATCH 6/8] Add Nadrieril's proposal to alternative approaches section. --- text/3921-place-traits.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/text/3921-place-traits.md b/text/3921-place-traits.md index 9b56de048ea..83ba79d7008 100644 --- a/text/3921-place-traits.md +++ b/text/3921-place-traits.md @@ -218,6 +218,22 @@ would add additional complexity in understanding the control flow of the resulti This could make understanding uses of these traits significantly more difficult for end users of types implement these traits. +### Nadrieril custom_refs proposal + +Similar functionality is also being discussed as part of the [custom reference proposal +orignally created by Nadrieril](https://hackmd.io/N0sjLdl1S6C58UddR7Po5g). This is also +fits in the category of significantly more complicated traits. However, the very large +amount of additional affordances it could offer may make it more worth it in this +specific case. + +This RFC still prefers the simpler approach, as pretty much all of the Nadrieril +proposal would need to be implemented to get `Place`-like behavior. This would bring +significant implementation effort and risk. + +If desired, the functionality of this proposal can at a latter time be reimplemented +through the trait in that proposal, meaning that this can be seen as an intermediate +step. + ### Limited macro based trait Going the other way in terms of complexity, a `Place` trait with constraints on how the From 553f149911e33d4554e0586bf5bf23a116a4a4c5 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Mar 2026 14:10:06 +0100 Subject: [PATCH 7/8] Rename trait to DerefMove to preserve Place for the nadrieril proposal. --- text/3921-place-traits.md | 74 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/text/3921-place-traits.md b/text/3921-place-traits.md index 83ba79d7008..008a5d1967b 100644 --- a/text/3921-place-traits.md +++ b/text/3921-place-traits.md @@ -1,4 +1,4 @@ -- Feature Name: `place_traits` +- Feature Name: `deref_move_trait` - Start Date: 2026-01-23 - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/3921) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) @@ -6,7 +6,7 @@ ## Summary [summary]: #summary -This RFC introduces the `Place` trait. This trait allows arbitrary types to implement the +This RFC introduces the `DerefMove` trait. This trait allows arbitrary types to implement the special derefence behavior of the `Box` type. In particular, it allows an arbitrary type to act as an owned place allowing values to be (partially) moved out and moved back in again. @@ -34,19 +34,19 @@ in the context of an implementation of garbage collection. ## Guide-level explanation [guide-level-explanation]: #guide-level-explanation -This proposal introduces a new unsafe trait `Place`: +This proposal introduces a new unsafe trait `DerefMove`: ```rust -unsafe trait Place: DerefMut { +unsafe trait DerefMove: DerefMut { fn place(&self) -> *const Self::Target; fn place_mut(&mut self) -> *mut Self::Target; } ``` -The `Place` trait allows values of the type to be treated as a `Box` with a content. That +The `DerefMove` trait allows values of the type to be treated as a `Box` with a content. That is, they behave like a variable of type `Deref::Target`, just (potentially) stored in a different location than the stack. The contents can be (partially) moved in and out of dereferences of the type, with the borrow checker ensuring soundness of the resulting -code. As an example, if `Foo` implements `Place` for type `Bar`, the following would be +code. As an example, if `Foo` implements `DerefMove` for type `Bar`, the following would be valid rust code: ```rust fn baz(mut x: Foo) -> Foo { @@ -56,38 +56,38 @@ fn baz(mut x: Foo) -> Foo { } ``` -In implementing the `Place` trait, the type transfers responsibility for managing its +In implementing the `DerefMove` trait, the type transfers responsibility for managing its content to the compiler. In particular, the type should not assume the contents are -initialized when `Place::place` and `Place::place_mut` are called. +initialized when `DerefMove::place` and `DerefMove::place_mut` are called. It also transfers responsibiltiy for dropping the contents to the compiler. This will be done in drop glue, and the `Drop::drop` function will therefore see the contents as uninitialized. -The transfer of responsibility also puts requirements on the implementer of `Place`. In +The transfer of responsibility also puts requirements on the implementer of `DerefMove`. In particular, instances of the type where the contents are uninitialized through a means other than moving out of a dereference should not be created. Breaking this rule and dereferencing the resulting instance is immediate undefined behavior. -There is one oddity in the behavior of types implementing `Place` to be aware of. +There is one oddity in the behavior of types implementing `DerefMove` to be aware of. Automatically elaborated dereferences of values of such types will always trigger an abort on panic, instead of unwinding when that is enabled. However, generic types constrained to -only implement Deref or DerefMut but not Place will always unwind on panics during -dereferencing, even if the underlying type also implements Place. +only implement `Deref` or `DerefMut` but not `DerefMove` will always unwind on panics during +dereferencing, even if the underlying type also implements `DerefMove`. ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This proposal introduces one new main language item, the traits `Place`. We also introduce +This proposal introduces one new main language item, the traits `DerefMove`. We also introduce a number of secondary language items which are used to make implementation easier and more robust, which we shall define as they come up below. -Instances of a type implementing the trait Place shall provide a "contents" of type +Instances of a type implementing the trait `DerefMove` shall provide a "contents" of type `Deref::Target`, which shall act as a place for borrow checking. The implementer of the -`Place` trait shall ensure that: -- The `Place::place` and `Place::place_mut` return pointers to the storage of the content +`DerefMove` trait shall ensure that: +- The `DerefMove::place` and `DerefMove::place_mut` return pointers to the storage of the content valid for the lifetime of the self reference. -- The `Place::place` and `Place::place_mut` functions shall not assume the contents to +- The `DerefMove::place` and `DerefMove::place_mut` functions shall not assume the contents to be initialized - The `Drop::drop` function shall not interact with the content, but only with its storage. - With the exception of uninitialization through moving out of a dereference of the instance, @@ -96,19 +96,19 @@ Instances of a type implementing the trait Place shall provide a "contents" of t In return, the compiler shall guarantee that: - When the contents are uninitialized through moving out of a dereference of an instance, - references to that instance will only be available through invocations of `Place::place`, - `Place::place_mut` and `Drop::drop` by the compiler itself. + references to that instance will only be available through invocations of `DerefMove::place`, + `DerefMove::place_mut` and `Drop::drop` by the compiler itself. - The contents shall be dropped or moved out of by the compiler before invoking `Drop::drop` of the instance. -Furthermore, as there seems to be no good reasons for `Place::place` and `Place::place_mut` +Furthermore, as there seems to be no good reasons for `DerefMove::place` and `DerefMove::place_mut` to panic, the compiler shall handle panics in non-explicit calls to these functions by aborting. This allows the compiler to compile code more efficiently and provides more avenues for optimizations. Note that the above requirements explicitly allow changes in the storage location of the -contents. This is only restricted by the contract imposed by `Place` during the lifetime -of self pointers passed to `Place::place` and `Place::place_mut`. +contents. This is only restricted by the contract imposed by `DerefMove` during the lifetime +of self pointers passed to `DerefMove::place` and `DerefMove::place_mut`. As a consequence, `Pin` does not automatically satisfy all the requirements of Pin when Foo implements place. This will need to be verified on an implementation by @@ -116,17 +116,17 @@ implementation basis. ### Implementation details -Given the above contract, dereferences of a type implementing `Place` can be lowered +Given the above contract, dereferences of a type implementing `DerefMove` can be lowered directly to MIR, only being elaborated after borrow checking. This allows the borrow checker and drop elaboration logic to provide the guarantees above, and ensure that dereferences are sound in the pressence of moves out of the contents. The dereferences and drops of the contained value can be elaborated in the passes after borrow checking. This process will be somewhat similar to what is already done for Box, -with the difference that dereferences of types implementing `Place` may panic. These +with the difference that dereferences of types implementing `DerefMove` may panic. These panics will be handled by aborting, avoiding significant changes in the control flow graph. -In order to generate the function calls to the `Place::place` and `Place::place_mut` +In order to generate the function calls to the `DerefMove::place` and `DerefMove::place_mut` during the dereference elaboration we propose making these functions additional language items. @@ -156,7 +156,7 @@ has, which is considered acceptable in its current state. ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -Ideas for something like the `Place` trait design here can be found in past discussions of +Ideas for something like the `DerefMove` trait design here can be found in past discussions of DerefMove traits and move references. The desire for some way of doing move derefences goes back to at least https://github.com/rust-lang/rfcs/issues/997. @@ -203,13 +203,13 @@ attractive on the whole. ### More complicated place traits -Several more complicated `Place` traits have been proposed by tema2 in two threads on the +Several more complicated `DerefMove` traits have been proposed by tema2 in two threads on the internals forum: - [DerefMove without `&move` refs](https://internals.rust-lang.org/t/derefmove-without-move-refs/17575) - [`DerefMove` as two separate traits](https://internals.rust-lang.org/t/derefmove-as-two-separate-traits/16031) These traits aimed at providing more feedback with regards to the length of use of the -pointer returned by the `Place::place` method, and the status of the value in that +pointer returned by the `DerefMove::place` method, and the status of the value in that location after use. Such a design would open up more possible use cases, but at the cost of significantly more complicated desugarings. @@ -227,7 +227,7 @@ amount of additional affordances it could offer may make it more worth it in thi specific case. This RFC still prefers the simpler approach, as pretty much all of the Nadrieril -proposal would need to be implemented to get `Place`-like behavior. This would bring +proposal would need to be implemented to get `DerefMove`-like behavior. This would bring significant implementation effort and risk. If desired, the functionality of this proposal can at a latter time be reimplemented @@ -236,11 +236,11 @@ step. ### Limited macro based trait -Going the other way in terms of complexity, a `Place` trait with constraints on how the +Going the other way in terms of complexity, a `DerefMove` trait with constraints on how the projection to the actual location to be dereferenced was proposed in [another internals forum thread](https://internals.rust-lang.org/t/derefmove-without-move-references-aka-box-magic-for-user-types/19910). -This proposal effectively constrains the `Place::deref` method to only doing field -projections and other dereferences. The advantage of this is that such a trait has far +This proposal effectively constrains the `DerefMove::place` method to only doing field +projections and other dereferences. The advantage of this is that such a trait has less severe safety implications, and by its nature cannot panic making its use more predictable. @@ -272,17 +272,17 @@ a special way. In terms of implementability, a small experiment has been done implementing the deref elaboration for an earlier version of this trait at https://github.com/davidv1992/rust/tree/place-experiment. That implementation is -sufficiently far along to support running code using the Place trait, but does not yet -properly drop the internal value, instead leaking it. +sufficiently far along to support running code using the `DerefMove` trait, but does not yet +properly drop the content, instead leaking it. ## Unresolved questions [unresolved-questions]: #unresolved-questions -Right now, the design states that panic in calls to `Place::place` or `Place::place_mut` +Right now, the design states that panic in calls to `DerefMove::place` or `DerefMove::place_mut` can cause an abort when the call was generated in the MIR. This is done to make compilation and the resulting code more performant. It is however possible to implement these with proper unwinding, at the cost of generating a more complicated control flow graph before -borrow checking for code using the dereference behavior of `Place`. This would likely +borrow checking for code using the dereference behavior of `DerefMove`. This would likely result in longer compile times and less optimized results, however that could be judged to be a worthwhile tradeoff. @@ -302,5 +302,5 @@ interesting use cases are already available without it. Finally, there is potential for the trait as presented here to become useful in the in place initialization project. It could be a building block for generalizing things like partial initialization to smart pointers. This would require future design around an api -for telling the borrow checker about new empty values implementing Place, but that seems +for telling the borrow checker about new empty values implementing `DerefMove`, but that seems orthogonal to the design here. From 1d6028235216ee3dfb968d130e49e0c8964963d4 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Fri, 6 Mar 2026 14:13:38 +0100 Subject: [PATCH 8/8] Minor spelling fixes. --- text/3921-place-traits.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/3921-place-traits.md b/text/3921-place-traits.md index 008a5d1967b..68d78dcad18 100644 --- a/text/3921-place-traits.md +++ b/text/3921-place-traits.md @@ -7,7 +7,7 @@ [summary]: #summary This RFC introduces the `DerefMove` trait. This trait allows arbitrary types to implement the -special derefence behavior of the `Box` type. In particular, it allows an arbitrary type +special dereference behavior of the `Box` type. In particular, it allows an arbitrary type to act as an owned place allowing values to be (partially) moved out and moved back in again. @@ -60,7 +60,7 @@ In implementing the `DerefMove` trait, the type transfers responsibility for man content to the compiler. In particular, the type should not assume the contents are initialized when `DerefMove::place` and `DerefMove::place_mut` are called. -It also transfers responsibiltiy for dropping the contents to the compiler. This will +It also transfers responsibility for dropping the contents to the compiler. This will be done in drop glue, and the `Drop::drop` function will therefore see the contents as uninitialized. @@ -119,7 +119,7 @@ implementation basis. Given the above contract, dereferences of a type implementing `DerefMove` can be lowered directly to MIR, only being elaborated after borrow checking. This allows the borrow checker and drop elaboration logic to provide the guarantees above, and ensure that -dereferences are sound in the pressence of moves out of the contents. +dereferences are sound in the presence of moves out of the contents. The dereferences and drops of the contained value can be elaborated in the passes after borrow checking. This process will be somewhat similar to what is already done for Box, @@ -141,7 +141,7 @@ could result in undefined behavior that is difficult to find. Second, with the current design the underlying type is no longer aware of whether or not the space it has allocated for the value is populated or not. This inhibits functionality which would use this information on drop to automate removal from a container. Note -however that such usecases can use a workaround with the user explicitly requesting +however that such use cases can use a workaround with the user explicitly requesting removal before being able to move out of a smart-pointer like type. Finally, the type does not have runtime-awareness of when the value is exactly added. @@ -157,7 +157,7 @@ has, which is considered acceptable in its current state. [rationale-and-alternatives]: #rationale-and-alternatives Ideas for something like the `DerefMove` trait design here can be found in past discussions of -DerefMove traits and move references. The desire for some way of doing move derefences +`DerefMove` traits and move references. The desire for some way of doing move dereferences goes back to at least https://github.com/rust-lang/rfcs/issues/997. The rationale behind the current design is that it explicitly sticks very closely to what @@ -168,7 +168,7 @@ implementation phase. ### DerefMove trait -Designs based on a simpler DerefMove trait have been previously proposed in the unmerged +Designs based on a simpler `DerefMove` trait have been previously proposed in the unmerged [RFC2439](https://github.com/rust-lang/rfcs/pull/2439) and an [internals forum thread](https://internals.rust-lang.org/t/derefmove-without-move-why-dont-we-have-it/19701). These come down to a trait of the form ``` @@ -221,7 +221,7 @@ users of types implement these traits. ### Nadrieril custom_refs proposal Similar functionality is also being discussed as part of the [custom reference proposal -orignally created by Nadrieril](https://hackmd.io/N0sjLdl1S6C58UddR7Po5g). This is also +originally created by Nadrieril](https://hackmd.io/N0sjLdl1S6C58UddR7Po5g). This is also fits in the category of significantly more complicated traits. However, the very large amount of additional affordances it could offer may make it more worth it in this specific case.