TLDR: this is a mini-proposal/pre-RFC about enabling #[derive(From)]
for newtype wrappers (tuple structs with a single field).
Note that this is not fully structured as an RFC yet, I just wanted to get a vibe check first.
Motivation
I think that the primary motivation should just be to "fill in the missing blanks" where the solution is obvious, but I'll show a concrete use-case.
A common idiom to improve the type safety of Rust code is to use newtype wrappers on top of another type. For example, we can wrap a number to represent things like Priority(i32)
, PullRequestNumber(u32)
or TCPPort(u16)
.
When implementing such a wrapper, it is useful to expose certain operations on the newtype that delegate to the inner type. Things like PartialEq
, Debug
, Copy/Clone
, etc. That is usually done using the built-in derive macro:
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy, Clone)]
struct TcpPort(u16);
Which is quite terse and doesn't require any macros or third-party crates.
However, not all standard library traits can be derived in this way. Notably, it is not possible to derive an implementation of the From
trait. In some cases, where the newtype should validate its contents (e.g. struct Email(String)
), From
is not desirable, but in many cases the newtype can accept any value of the inner type, and thus implementing From
for it is quite natural.
Writing the implementation by hand is easy, and takes just ~4-5 lines of code. However, when you want to use many newtype wrappers in your codebase, this can get annoying quickly. In that situation I usually either:
- Just use a type alias instead and tell myself that I will convert it to a newtype "later" (which often doesn't ever happen).
- Create a small declarative macro that generates the boilerplate for me. But creating a macro obfuscates the code and requires me to e.g. determine some a way to tell the macro which specific traits should be implemented (for example for some newtypes I don't want to implement
Eq
).
Proposal
From
cannot be currently derived because it is unclear how it should behave in all situations where deriving is possible (for enums, struct with named fields, struct with multiple fields etc.). I propose to enable #[derive(From)]
in a single place - when used on a tuple struct with a single field. In this case, it is 100% clear what should the compiler do, and there is (in my opinion) no question about how should such a derive be expanded.
So this:
#[derive(From)]
struct TcpPort(u16);
would generate the following code:
struct TcpPort(u16);
impl From<u16> for TcpPort {
fn from(value: u16) -> Self {
Self(value)
}
}
For all other places (tuple structs with zero or multiple fields, structs with named fields, enums, etc.), #[derive(From)]
would still be disabled.
This is forward-compatible, as we can expand the set of places where #[derive(From)]
will be allowed in the future.
For example, we could allow using it for structs with multiple fields, where a single field would be annotated with
#[from]
and the rest would implementDefault
. But that's something that is much more bikeshed-worthy, so I explicitly leave out of this proposal.
It shouldn't run into any orphan rule problems, because at the place of expansion, we necessarily own the type for which we implement From
.
As a part of this change, we could maybe also enable it for structs with a single named field, but that seems slightly less obvious to me, and it is also not so common for the newtype pattern.
A related functionality could be deriving Into
(or rather From<Newtype> for <InnerType>
to convert back into the inner type), but again, that's more bikeshed-worthy, so I wouldn't include it in this RFC.
Prior art
-
The popular derive_more crate enables deriving From for single-field tuple structs. It even mentions the same motivation:
Rust has lots of builtin traits that are implemented for its basic types, such as Add, Not, From or Display. However, when wrapping these types inside your own structs or enums you lose the implementations of these traits and are required to recreate them. This is especially annoying when your own structures are very simple, such as when using the commonly advised newtype pattern (e.g. MyInt(i32)). This library tries to remove these annoyances and the corresponding boilerplate code. It does this by allowing you to derive lots of commonly used traits for both structs and enums.
Apart from the single-field tuple struct variant, it also supports other use-cases, e.g. deriving
From
on enums.- Crates that currently use
derive_more
only for the simplest (but likely most common) use-case with single-field tuple structs could instead just use the built-in derive.
- Crates that currently use
-
derive-from-one offers similar, although less comprehensive, functionality.
-
This idea was floated around in New derives: From and Deref · Issue #2026 · rust-lang/rfcs · GitHub, but it was combined with deriving the
Deref
trait, and I think that it was larger in its scope.
Was there any prior discussion about this? I haven't found anything apart from the one issue above. I would be glad for any feedback.