Derivative

Customizable alternative derive attributes for Rust (Debug, Clone, PartialEq, Hash, Default)

Library
Cargo
v2.2.0
469stars
MIT/Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
35/100Needs Attention
Development Activity0
Maintenance0
Community60
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture74
Code Quality76
Innovation68
Learning Curve70

Derivative provides a #[derive(Derivative)] proc-macro that replaces Rust’s standard #[derive(Debug, Clone, PartialEq, ...)] with customizable equivalents. Where the standard derives are all-or-nothing, Derivative lets you skip specific fields, override how a field is formatted or compared, and control generic trait bounds per-field rather than relying on the compiler’s often-too-strict automatic bound inference.

Common uses include excluding a sensitive or non-Debug field from a Debug implementation, ignoring a cache/derived field from PartialEq/Hash comparisons, or fixing generic structs where the standard #[derive] adds unnecessary trait bounds on type parameters that aren’t actually required for the derived impl to compile.

What You Get

  • #[derivative(Debug)] with per-field Debug="ignore" or custom formatting functions
  • #[derivative(Clone)], PartialEq, PartialOrd, and Hash with the same field-level override model
  • #[derivative(Default)] for specifying non-standard default values per field
  • Explicit control over generic trait bounds instead of relying on the compiler’s automatic (and sometimes overly strict) bound inference
  • A use_core feature for #![no_std] environments

Common Use Cases

  • Excluding a sensitive, non-Debug, or noisy field from a struct’s Debug output
  • Ignoring a cached or derived field from PartialEq/Hash so equality reflects only logical identity
  • Fixing a generic struct where standard #[derive(Clone)] incorrectly requires T: Clone on a type parameter that isn’t actually stored by value
  • Providing custom Default values for individual fields instead of relying on each field’s own Default impl

Under The Hood

Architecture: The crate is a proc-macro = true library whose entry parses #[derivative(...)] attributes (src/attr.rs, 887 lines — the largest module, handling per-field and per-struct attribute parsing) and dispatches to per-trait code generators: src/debug.rs, src/clone.rs, src/cmp.rs (PartialEq/PartialOrd/Ord, 407 lines), src/hash.rs, and src/default.rs, each emitting a manual trait impl equivalent to what #[derive] would generate but honoring the custom attributes. src/bound.rs computes the correct generic trait bounds per field rather than the blanket bounds the standard derive macros apply, and src/ast.rs provides a shared intermediate representation across all the trait generators.

Tech Stack: Standard Rust proc-macro toolchain — proc-macro2, quote for code generation, and syn 1.0 (with visit/extra-traits features) for parsing. No runtime dependencies are pulled into the compiled binary since all work happens at compile time; the crate itself has an MSRV of Rust 1.34, explicitly called out in the README as a semver-relevant guarantee.

Code Quality: The tests/ directory (71 files) is unusually extensive for a crate of this size, covering each derived trait across generic bounds, packed structs, transparent representations, and compile-fail cases (tests/compile-fail/) verified via trybuild, plus runtime-macros-derive for macro-expansion testing. This breadth reflects the correctness bar needed for a crate that hand-generates trait impls meant to be indistinguishable from the compiler’s own #[derive] output. Development has slowed significantly (last commit September 2025) but the crate remains stable and semver-compliant per its own documentation.

API Design: The attribute syntax deliberately mirrors serde’s well-known attribute model (the README credits serde as inspiration), so Rust developers already familiar with #[serde(...)] field attributes can pick up #[derivative(...)] with minimal new concepts — replace #[derive(Debug)] with #[derive(Derivative)] #[derivative(Debug)] and add per-field overrides only where the default derive behavior is insufficient.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search