Library / SDK
JelteF/derive_more avatar
JelteF/derive_more

derive_more: derive macros for the traits Rust leaves out

Some more derive(Trait) options

2,146 stars153 forksRustMIT

At a glance

What is it?
derive_more fills the gap between Rust's builtin derives and the boilerplate of the newtype pattern. It is a procedural macro crate with per-derive feature flags, an MIT licence, and one hygiene rule worth knowing before you re-export it.
Who is it for?
Adopt derive_more when your crate is mostly newtypes and enums that need From, Display, Add or Error, and you would rather not hand-write those impls. Skip it if you only need one or two derives, since each enabled feature is a separate compilation unit you pay for.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 24 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The newtype pattern and the impls it costs you

Rust gives Add, Not, From and Display to its primitive types. Wrap i32 in a struct and those implementations do not follow. The README frames the problem exactly that way: wrapping a type inside your own struct or enum means you "lose the implementations of these traits and are required to recreate them." For a type like MyInt(i32), the recreation is mechanical, and the mechanical part is what derive_more removes.

The target audience is narrow and specific. If you write domain types that wrap a single field, or enums where each variant carries a value and you want to convert into and out of them, this crate is aimed at you. If your types are large records with no conversion or operator semantics, deriving Add or Display for them would be a design mistake, and no macro can fix that. The crate is a boilerplate remover, not a modelling tool.

What actually gets generated for each derive

The README is explicit that understanding the generated code matters, and it points readers at each derive's own documentation plus the cargo-expand utility, which shows your source with all macros and derives expanded. That is the right instinct: a derive macro is a code generator, and you should read what it wrote.

The derives are grouped by purpose. Conversion covers From, Into, FromStr, TryFrom, TryInto, IntoIterator, AsRef and AsMut. Formatting covers Debug and the Display-like family, which the README lists as Display, Binary, Octal, LowerHex, UpperHex, LowerExp, UpperExp and Pointer. Error handling is a single Error derive. Operator overloading is the largest group: Index, Deref, the Not-like pair of Not and Neg, the Add-like family of Add, Sub, BitAnd, BitOr and BitXor, the Mul-like family of Mul, Div, Rem, Shr and Shl, Sum and Product, IndexMut, DerefMut, the AddAssign-like and MulAssign-like families, and Eq with PartialEq. Hash sits on its own. Four derives generate static methods rather than trait impls: Constructor, which makes a new method, plus IsVariant, Unwrap and TryUnwrap, which produce is_foo, unwrap_foo and try_unwrap_foo methods per enum variant.

The grouping matters because of a warning in the README: deriving one trait does not pull in its relatives. Writing #[derive(Mul)] will not derive Div, and you must write #[derive(Mul, Div)] to get both. A reader who assumes the "-like" families behave as a unit will get compile errors that look mysterious until they find that note.

Installing derive_more and deriving your first trait

The crate is published on crates.io and installed as a normal Cargo dependency. The README's key point about installation is that no derives are supported by default, specifically to avoid redundant compilation time, so you enable each type of derive as a feature.

toml
[dependencies]
derive_more = { version = "2", features = ["from", "add", "into_iterator"] }

If compilation time is not a concern, the README offers the full feature instead, which enables support for every derive the crate provides.

toml
derive_more = { version = "2", features = ["full"] }

For no_std targets the README says to disable default features, because the only default feature is std. It also notes that this combines with full.

toml
derive_more = { version = "2", default-features = false }

With the dependency in place, the README's example derives From, Add, Display and Into on a newtype and on an enum, then asserts on the results. The enum shows the display attribute syntax, where a variant can carry a format string such as "int: {_0}" or a fixed string like "nothing".

rust
use derive_more::{Add, Display, From, Into};

#[derive(PartialEq, From, Add)]
struct MyInt(i32);

#[derive(PartialEq, From, Add, Display)]
enum MyEnum {
    #[display("int: {_0}")]
    Int(i32),
    Uint(u32),
    #[display("nothing")]
    Nothing,
}

What a reader should expect is that MyInt(5) + 6.into() produces MyInt(11), and that MyEnum::Int(15).to_string() yields "int: 15". If the code does not compile, the first thing to check is whether the corresponding feature is enabled in Cargo.toml, since the default build has none of them.

Feature flags, compilation cost and the no_std path

The feature list in the crate's Cargo.toml is long and flat: add, add_assign, as_ref, constructor, debug, deref, deref_mut, display, eq, error, from, from_str, hash, index and more, each forwarding to a matching feature on the derive_more-impl crate. That structure tells you where the cost lives. The root crate is a thin wrapper, and the implementation crate is a workspace member that carries the actual macro logic. Enabling a feature pulls in that derive's code and nothing else.

This is a real trade-off rather than a neutral design. A project that uses six derives writes six feature names into its manifest and has to keep them in sync as the code changes; a missing feature produces a derive that simply is not available. The full feature removes that bookkeeping at the cost of compiling everything, which is the opposite of what the README's default exists to achieve. Neither choice is wrong, but teams should pick one deliberately rather than drifting into full because a feature name was forgotten.

The no_std path is supported through default-features = false, and the crate's categories include no-std. The README frames std as the only default feature, so disabling defaults is the whole of the no_std setup. The Cargo.toml also shows a rustc_version build dependency that is optional, and the package metadata declares rust-version = "1.81.0", so the minimum supported toolchain is stated in the manifest rather than left to prose.

Hygiene and re-exporting the macros from your own crate

Macro hygiene is where derive_more makes a demand that surprises people who wrap it. The README states that, for hygiene purposes, the macros use derive_more::* absolute paths in their expansions. A downstream crate that imports a re-exported derive from your library, without depending on derive_more directly, will fail to compile with "could not find derive_more in the list of imported crates".

The README's fix is to re-export the module as well as the macro, so consumers write use my_lib::{derive_more, Display}; and the expansion can resolve its absolute path. This is not a bug to work around; it is the cost of generated code that refers to its own crate by absolute path. If you maintain a library that re-exports these derives, treat the module re-export as part of your public API and document it. If you only use the derives inside your own crate, the rule never applies to you.

The README also explains the import surface. The crate root exports derive macros only, the derive module exports all macros only, and the with_trait module re-exports the standard library traits alongside the derives, so use derive_more::with_trait::Display brings both the macro and core::fmt::Display into scope.

Where derive_more is the wrong tool

The Error derive is the clearest case to think about before adopting. It generates an error type from an enum, which is useful, but the README's own framing of the crate is boilerplate removal for simple structures. If your error type needs source chaining, backtraces, context propagation or a builder around it, the derive gives you a plain implementation and you will be writing much of the surrounding machinery yourself. The README points readers of Constructor at the derive-new crate when more customisation is needed, which is the same admission in a different place: the static-method derives are described as very basic.

Operator derives have a subtler failure mode. Deriving Add for a newtype is fine when addition is meaningful for the wrapped value. Derive it for a type where the operation is not associative, or where the wrapped field is not the only field, and the generated impl encodes a decision nobody reviewed. The macro will not stop you. Similarly, deriving Deref and DerefMut on a wrapper turns the wrapper into a transparent view of its field, which erases the distinction the newtype existed to create. The derives are available; whether they express your intent is a separate question the compiler will not ask.

Finally, there is no documented rollback story. The README does not describe how to migrate away from a derive once it is in place, because the answer is simply to write the impl by hand, but nothing in the README covers a staged removal. Plan for that as ordinary refactoring work.

How derive_more differs from thiserror

The comparison readers most often want is with thiserror, and the difference is scope rather than quality. thiserror exists to produce error types: you describe variants, their display messages and their sources, and it generates the Error and Display impls plus the From conversions that make the question mark operator work. derive_more does that too through its Error derive, but errors are one group among many.

The rest of derive_more has no counterpart in an error-focused crate. Operator overloading for a newtype, Deref and DerefMut, Index and IndexMut, the Sum and Product derives, IntoIterator, the Display-like formatting family, and the static-method derives IsVariant, Unwrap and TryUnwrap all sit outside the error-handling problem. A project that needs both an error enum and arithmetic on a wrapped integer ends up with two dependencies, or with derive_more alone. A project that needs only error types and nothing else can use the narrower crate and enable a single feature set rather than reasoning about the feature list here.

The direction of the comparison also matters for review. derive_more generates many different kinds of impl from one attribute, so the blast radius of a mistaken derive is larger than in a crate with one job. That is an argument for reading the expanded output with cargo-expand on the types that matter, not an argument against the crate.

Editorial conclusion

Adopt derive_more when your crate is mostly newtypes and enums that need From, Display, Add or Error, and you would rather not hand-write those impls. Skip it if you only need one or two derives, since each enabled feature is a separate compilation unit you pay for. Before committing, check the feature list in the crate's Cargo.toml against the derives you actually use, confirm your toolchain is at least Rust 1.81 because the manifest sets rust-version = "1.81.0", and decide up front whether downstream crates need the derive_more module re-exported alongside the macros.

Frequently asked questions

What is derive in Rust?

A derive is an attribute that asks the compiler to generate a trait implementation for a type, such as #[derive(PartialEq, From, Add)] on a struct. Rust ships derives for a few traits, and derive_more adds derives for many more, including From, Into, Display, Add and Error.

How do I install derive_more in a Rust project?

Add it to the dependencies section of Cargo.toml with the features you need, for example derive_more = { version = "2", features = ["from", "add", "into_iterator"] }. No derives are enabled by default, so a bare dependency without features gives you nothing to derive.

Does derive_more work in no_std environments?

Yes. The README says to disable default features for no_std, because the only default feature is std, and notes that this can be combined with the full feature to get every derive without std.

Why does deriving Display from a re-exported derive_more macro fail to compile?

The macros use derive_more::* absolute paths in their expansions for hygiene, so a downstream crate that only imports the re-exported macro cannot resolve derive_more. The README's fix is to re-export the derive_more module alongside the macro and import both.

Official sources

  1. Issues
  2. JelteF/derive_more on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jeltef-derive-more.svg)](https://hysenlabs.com/projects/jeltef-derive-more)