From 3206f4cc16d33941332d948a2dcbb41d658df8e9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 22 Jan 2026 16:57:32 +0100 Subject: [PATCH 1/4] Add `derive(Deref)` RFC --- text/0000-derive-deref.md | 419 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 text/0000-derive-deref.md diff --git a/text/0000-derive-deref.md b/text/0000-derive-deref.md new file mode 100644 index 00000000000..69c507d08fb --- /dev/null +++ b/text/0000-derive-deref.md @@ -0,0 +1,419 @@ +- Feature Name: `derive_deref` +- Start Date: 2026-01-22 +- RFC PR: [rust-lang/rfcs#3911](https://github.com/rust-lang/rfcs/pull/3911) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +Allow deriving an implementation of the `Deref` trait using `#[derive(Deref)]` on structs and enums. + +```rust +#[derive(Deref)] +struct TcpPort(u16); + +// Generates: +impl Deref for TcpPort { + type Target = u16; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Deref)] +struct Info { + inner: u16, +} + +// Generates: +impl Deref for Info { + type Target = u16; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +#[derive(Deref)] +enum Enum { + V1(u16), + V2 { field: u16 }, +} + +// Generates: +impl Deref for Enum { + type Target = u16; + + fn deref(&self) -> &Self::Target { + match self { + Self::V1(v) => v, + Self::V2 { field } => field, + } + } +} +``` + +If there is more than one field, a `#[deref]` attribute will be required on the field we want to "deref": + +```rust +#[derive(Deref)] +struct TcpPort(u16, #[deref] u16); + +// Generates: +impl Deref for TcpPort { + type Target = u16; + + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +#[derive(Deref)] +struct Info { + a: u16, + #[deref] + b: u16, +} + +// Generates: +impl Deref for Info { + type Target = u16; + + fn deref(&self) -> &Self::Target { + &self.b + } +} + +#[derive(Deref)] +enum Enum { + V1(u16), + V2 { #[deref] a: u16, b: u32 }, +} + +// Generates: +impl Deref for Enum { + type Target = u16; + + fn deref(&self) -> &Self::Target { + match self { + Self::V1(v) => v, + Self::V2 { a, b } => a, + } + } +} +``` + +# Motivation +[motivation]: #motivation + +The primary motivation is to remove one of the gaps in the Rust language which prohibit combining language features in intuitive ways. Both the `#[derive(Trait)]` macro and the `Deref` trait are pervasively used across the Rust ecosystem, but it is currently not possible to combine them, even in situations where the resulting behavior seems *completely obvious*. + +Concretely, when you have a struct with a single field and want to implement the `Deref` trait to allow getting access to a field or field's method of the type without needing to do explicitly access the field first, `#[derive(Deref)]` seems like the most intuitive way of achieving that. `Deref` is a standard library trait, `#[derive(Trait)]` works with many other such traits (such as `Hash`, `Eq`, `Clone`, etc.), and there is essentially only one possible implementation that makes sense. However, when users currently try to do that, they are met with a compiler error. + +Enabling this would make one more intuitive use-case in the language "just work", and would reduce boilerplate that Rust users either write over and over again or for which they have to use macros or external crates. + +## Newtype pattern +As a concrete use-case, `#[derive(Deref)]` is particularly useful in combination with the very popular [newtype pattern](https://doc.rust-lang.org/rust-by-example/generics/new_types.html). In this pattern, an inner type is wrapped in a new type (hence the name), typically a tuple struct, to semantically make it a separate concept in the type system and thus make it harder to mix unrelated types by accident. For example, we can wrap a number to represent things like `Priority(i32)`, `PullRequestNumber(u32)` or `TcpPort(u16)`. + +When using the newtype pattern, it is common to implement standard library traits for it by delegating to the inner type. This is easily achievable with `#[derive]`: + +```rust +#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)] +struct UserId(u32); +``` + +However, not all standard library traits can be derived in this way, including the `Deref` trait. Currently, users have to write the boilerplate `Deref` implementation by hand. If there are many newtypes in a crate, this might lead users to implement a macro, which unnecessarily obfuscates the code, or use an external crate to derive the implementation, which increases code size and compile times. + +Is `Deref` really so useful for newtypes? Newtypes are used to make it more explicit what a type is about, like `PullRequestNumber(u32)`. Or to allow to implement traits on external types, like implementing `Drop` on an integer representing a system ID. However, a lot of time, the wrapping "gets in the way" so you have to implement `Deref` to have access to the field without doing it explicitly. + +To summarize, if `Deref` was `derive`-able, it could reduce the need for using macros or external crates and increase the number of cases where `#[derive]` takes care of all required `impl`s for a given newtype. + +## Other cases +For other cases, like struct with multiple fields or enums, the `Deref` trait becomes useful when one field is the "main" information and the rest is "additional" information. For example: + +```rust +struct Id { + id: u32, + needs_to_be_released: bool, +} +``` + +In this case, the `id` field is the actual information we want to manipulate whereas the `needs_to_be_released` is only useful in specific contexts (like a `Drop` trait implementation). + +For enums, it's similar: + +```rust +enum Id { + Owned(u32), + Borrowed(u32), +} +``` + +In case it's `Owned`, then the `Drop` implementation will release the resource, otherwise it won't. However, the `u32` always represents the same data, whatever the variant. + +## Why does it make sense to derive `Deref`? +There are various "standard" traits defined in the Rust standard library that are pervasively used across the ecosystem. Currently, some of these traits can already be automatically derived, for example `Hash` or `Debug`. These traits can be derived automatically because they are composable; an implementation of the trait for a struct can be composed of the trait implementations of its fields. + +One reason why we might not want to enable automatic derive for a specific trait is when the implementation would not be *obvious*. For example, if we allowed deriving `Display`, it is unclear how should the individual field implementations be composed. Should they be separated with a newline? Or a comma? That depends on the given type. + +However, when deriving a `Deref` implementation for a struct with a single field, the implementation seems straightforward and *obvious* (simply wrap the inner type in the struct). It should thus be possible to automatically derive it. For other cases, the `#[deref]` attribute will remove any ambiguity. + +## How common is implementing and deriving `Deref`? + +[This](https://github.com/search?type=code&q=lang%3ARust+%2F%5C%5Bderive%5C%28.*%5CbDeref%5B%2C+%5C%29%5D%2F) GitHub Code Search query shows tens of thousands of occurrences of the `Deref` trait being derived, typically using the `derive_more` crate. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +You can use `#[derive(Deref)]` to automatically generate an implementation of the `Deref` trait for the given type, which will allow to implicitly have access to the derefed field: + +```rust +#[derive(Deref)] +struct UserId(u32); + +// Will generate: +impl Deref for UserId { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +``` + +If the type is generic over the type of the inner field, the `Deref` implementation will be also generic: + +```rust +#[derive(Deref)] +struct Id(T); + +// Will generate: +impl Deref for Id { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +``` + +For enums where all variants contain one field of the same type, it will give: + +```rust +#[derive(Deref)] +enum Id { + Owned(u32), + Borrowed { value: u32 }, +} + +// Will generate: +impl Deref for Id { + type Target = u32; + + fn deref(&self) -> &Self::Target { + match self { + Self::Owned(v) => v, + Self::Borrowed { value } => value, + } + } +} +``` + +If there any ambiguities because there are more than one field, then the user will need to add the `#[deref]` attribute on the field they want to be derefed: + +```rust +#[derive(Deref)] +struct UserId(#[deref] u32, u8); + +// Will generate: +impl Deref for UserId { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Deref)] +enum Id { + Owned(#[deref] u32, bool), + Borrowed { #[deref] value: u32, something_else: bool }, +} + +// Will generate: +impl Deref for Id { + type Target = u32; + + fn deref(&self) -> &Self::Target { + match self { + Self::Owned(v, _) => v, + Self::Borrowed { value, .. } => value, + } + } +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +Placing `#[derive(Deref)]` on a struct is always permissible as long as there is at least one field, otherwise it will produce a compilation error. If there are multiple fields and the `#[deref]` attribute is not used, it will produce a compilation error. If the `#[deref]` attribute is present more than once, it will produce a compilation error. + +Type we deref is `$t`; + +- If `$s` is a tuple struct with one field: + ```rust + impl ::std::ops::Deref for $s { + type Target = $t; + + fn deref(&self) -> &Self::Target { + self.0 + } + } + ``` +- If `$s` is a tuple struct with x fields when `$n` is the field with the `#[deref]` attribute: + ```rust + impl ::std::ops::Deref for $s { + type Target = $t; + + fn deref(&self) -> &Self::Target { + self.$n + } + } + ``` +- If `$s` is a struct with a named field `$f`: + ```rust + impl ::std::ops::Deref for $s { + type Target = $t; + + fn deref(&self) -> &Self::Target { + self.$f + } + } + ``` + + This is the same implementation if there are more than one field thanks to the `#[deref]` attribute. + +Placing `#[derive(Deref)]` on enums, requires for all its variants to share one common type, otherwise it'll produce a compiler error. If a variant has no field, it will produce a compilation error. If a variant has more than one field and the `#[deref]` attribute isn't used, it will produce a compilation error. If the `#[deref]` attribute is present more than once on a variant, it will produce a compilation error. + +An enum `$e`, with a first variant named `$v1` which is a tuple variant ; and a second variant named `$v2` which is struct-like variant with a field named `$f`: + +- Both variants only have one field: + ```rust + impl ::std::ops::Deref for $e { + type Target = u32; + + fn deref(&self) -> &Self::Target { + match self { + Self::$v1(v) => v, + Self::$v2 { $f } => $f, + } + } + } + ``` +- Both variants have two fields and use the `#[deref]` attribute on the first field of each variant: + ```rust + impl ::std::ops::Deref for $e { + type Target = u32; + + fn deref(&self) -> &Self::Target { + match self { + Self::$v1(v, _) => v, + Self::$v2 { $f, .. } => $f, + } + } + } + ``` + + +Using `#[derive(Deref)]` on unions produces a compilation error. + +# Drawbacks +[drawbacks]: #drawbacks + +While this does enable more Rust code to "just work", it also means that we should be able to produce high-quality error messages in the compiler, as it is trivial to detect how many fields a struct or an enum variant has. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Based on the popularity of the `derive_more` crate (discussed in [Prior art](#prior-art)), which had more than 125 million downloads when this RFC was proposed, it seems that there is a lot of appetite for extending the set of use-cases where deriving standard traits is allowed. This feature was discussed in the past [here](https://github.com/rust-lang/rfcs/issues/2026). + +The proposed change enables the usage of an existing feature (`#[derive]`) in more situations. It makes code easier to read by using an intuitive built-in feature instead of forcing users to write boilerplate code or use macros. + +As always, an alternative is to just not do this, in that case users would continue implementing `Deref` using boilerplate code, macros or external crates. + +Because the scope of the proposed change is quite minimal, it should be forward-compatible with designs that would make it work in more situations in the future (some ideas are discussed in [Future possibilities](#future-possibilities)). There is one potential (although unlikely) incompatibility discussed below. + +# Prior art +[prior-art]: #prior-art + +## Ecosystem crates +There are several crates that offer deriving the `Deref` trait. The most popular one is [derive_more](https://crates.io/crates/derive_more), which allows deriving several standard traits that are normally not derivable, including `Deref`, `Display` or `Add`. + +[`#[derive(derive_more::Deref)`](https://docs.rs/derive_more/latest/derive_more/derive.Deref.html) works in the same way as proposed in this RFC for structs with a single field. However, it can also be used for other kinds of structs and even enums and supports more complex use-cases. For example: +- For structs with multiple fields, you need to add the `#[deref]` attribute on the field to be the target of the `Deref` trait: + ```rust + #[derive(derive_more::Deref)] + struct Point(#[deref] i32, i32); + ``` +- You can use the `#[forward]` attribute to use the `Deref` target of the current derefed item. + ```rust + #[derive(Deref)] + #[deref(forward)] + struct MyBoxedInt(Box); + + // generates: + impl derive_more::core::ops::Deref for MyBoxedInt { + type Target = as derive_more::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + as derive_more::core::ops::Deref>::deref(&self.0) + } + } + ``` + +The design proposed by this RFC should be forward compatible with all features of `derive_more`[^enums], if we decided to adopt any of them in the future. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +Should we also add a new `DerefMut` derive, with the same conditions as described here for the `Deref` trait? It comes with new questions like, "what happens when you implement `Deref` manually but use `derive(DerefMut)` ; should it error when `Deref::Target` isn't the expected type?" Example: + +```rust +#[derive(DerefMut)] // should this error or rely on silent coercion of String to str? +pub struct S(String); + +impl Deref for S { + type Target = str; + fn deref(&self) -> &str { + &self.0 + } +} +``` + +# Future possibilities +[future-possibilities]: #future-possibilities + +## `#[forward]` attribute +In the future, we could extend the set of supported use-cases. For example, we could allow to have a "traversal" `Deref` when the target already implements `Deref`: + +```rust +#[derive(Deref)] +#[deref(forward)] +struct MyBoxedInt(Box); + +``` + +which would generate this impl: + +```rust +impl derive_more::core::ops::Deref for MyBoxedInt { + type Target = as derive_more::core::ops::Deref>::Target; + #[inline] + fn deref(&self) -> &Self::Target { + as derive_more::core::ops::Deref>::deref(&self.0) + } +} +``` + +This is similar to how [RFC#3107](https://rust-lang.github.io/rfcs/3107-derive-default-enum.html) extended the deriving of the `Default` trait using the `#[default]` attribute. From d8232b645b78dd9ce0473feef7e19c905b6bcf45 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 23 Jan 2026 12:00:14 +0100 Subject: [PATCH 2/4] Fix code examples and add new drawback case for raw/smart pointers --- text/0000-derive-deref.md | 50 +++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/text/0000-derive-deref.md b/text/0000-derive-deref.md index 69c507d08fb..9ec7a1c1927 100644 --- a/text/0000-derive-deref.md +++ b/text/0000-derive-deref.md @@ -267,7 +267,7 @@ Type we deref is `$t`; type Target = $t; fn deref(&self) -> &Self::Target { - self.0 + &self.0 } } ``` @@ -277,7 +277,7 @@ Type we deref is `$t`; type Target = $t; fn deref(&self) -> &Self::Target { - self.$n + &self.$n } } ``` @@ -287,7 +287,7 @@ Type we deref is `$t`; type Target = $t; fn deref(&self) -> &Self::Target { - self.$f + &self.$f } } ``` @@ -331,7 +331,33 @@ Using `#[derive(Deref)]` on unions produces a compilation error. # Drawbacks [drawbacks]: #drawbacks -While this does enable more Rust code to "just work", it also means that we should be able to produce high-quality error messages in the compiler, as it is trivial to detect how many fields a struct or an enum variant has. +## Compilation errors + +While this does enable more Rust code to "just work", it also means that we need to produce high-quality error messages in the compiler, otherwise it might be more confusing than helpful. + +## Smart pointers + +`#[derive(Deref)]` as described in this RFC will do the wrong thing for smart pointer types: + +```rust +#[derive(Deref)] +struct MyPtr1(Box); +``` + +`MyPtr1` will deref to `Box` rather than `T`, which is likely harmless but may reveal an implementation detail, increase the number of `*`s needed, or in the case of `derive(DerefMut)`, allow breaking an invariant. + +The `#[deref(forward)]` attribute should mitigate this case, but will require users to add it themselves. + +## Raw pointers + +If you use `#[derive(Deref)]` to dereference to a raw pointer, you will get a raw pointer, which can't be safely dereferenced: + +```rust +#[derive(Deref)] +struct MyPtr2(*mut T); +``` + +The intent would presumably be to dereference to `T`, but it's likely something to discuss if we extend `#[derive(Deref)]` with `#[deref(forward)]`. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -356,11 +382,10 @@ There are several crates that offer deriving the `Deref` trait. The most popular #[derive(derive_more::Deref)] struct Point(#[deref] i32, i32); ``` -- You can use the `#[forward]` attribute to use the `Deref` target of the current derefed item. +- You can use the `#[deref(forward)]` attribute to use the `Deref` target of the current derefed item. ```rust #[derive(Deref)] - #[deref(forward)] - struct MyBoxedInt(Box); + struct MyBoxedInt(#[deref(forward)] Box); // generates: impl derive_more::core::ops::Deref for MyBoxedInt { @@ -394,24 +419,23 @@ impl Deref for S { # Future possibilities [future-possibilities]: #future-possibilities -## `#[forward]` attribute +## `#[deref(forward)]` attribute In the future, we could extend the set of supported use-cases. For example, we could allow to have a "traversal" `Deref` when the target already implements `Deref`: ```rust #[derive(Deref)] -#[deref(forward)] -struct MyBoxedInt(Box); +struct MyBoxedInt(#[deref(forward)] Box); ``` which would generate this impl: ```rust -impl derive_more::core::ops::Deref for MyBoxedInt { - type Target = as derive_more::core::ops::Deref>::Target; +impl ::core::ops::Deref for MyBoxedInt { + type Target = as ::core::ops::Deref>::Target; #[inline] fn deref(&self) -> &Self::Target { - as derive_more::core::ops::Deref>::deref(&self.0) + as ::core::ops::Deref>::deref(&self.0) } } ``` From afa2fa2128bb405b45113b3870147f6501e9343c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 23 Jan 2026 12:03:51 +0100 Subject: [PATCH 3/4] Add possibility to add an integer for the `deref(forward)` attribute to dereference more than once --- text/0000-derive-deref.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/0000-derive-deref.md b/text/0000-derive-deref.md index 9ec7a1c1927..6b6f25531ea 100644 --- a/text/0000-derive-deref.md +++ b/text/0000-derive-deref.md @@ -440,4 +440,16 @@ impl ::core::ops::Deref for MyBoxedInt { } ``` +In case we want to deref more than once, we could allow a non-null positive integers value: + +```rust +#[derive(Deref)] // generated Deref::Target = T +pub struct CowArc<'a, T> { + #[deref(forward = 2)] // maybe `#[deref(count = 2)]`? + v: Cow<'a, Arc>, +} +``` + +In this case, `Deref` would give us `Cow<'a, Arc>`, but if we want `T`, we use to "go through" `Cow` and `Arc`, therefore dereferencing two more times, hence `#[deref(forward = 2)]`. + This is similar to how [RFC#3107](https://rust-lang.github.io/rfcs/3107-derive-default-enum.html) extended the deriving of the `Default` trait using the `#[default]` attribute. From 9897e01e851443c8a02b286ca95461e7b147a350 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 23 Jan 2026 15:03:10 +0100 Subject: [PATCH 4/4] Improve wording for positive integers --- text/0000-derive-deref.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-derive-deref.md b/text/0000-derive-deref.md index 6b6f25531ea..fbe33f9a821 100644 --- a/text/0000-derive-deref.md +++ b/text/0000-derive-deref.md @@ -440,7 +440,7 @@ impl ::core::ops::Deref for MyBoxedInt { } ``` -In case we want to deref more than once, we could allow a non-null positive integers value: +In case we want to deref more than once, we could allow a positive integer value: ```rust #[derive(Deref)] // generated Deref::Target = T