num-derive
Procedural derive macros that implement num-traits' numeric traits — FromPrimitive, ToPrimitive, Zero, One, and more — on your own Rust enums and structs.
Repository Health
Technical Analysis
num-derive is a companion crate to num-traits that supplies #[derive(...)] macros for the numeric traits Rust’s standard library and the num ecosystem expect. Instead of hand-writing impl FromPrimitive for MyEnum or impl Zero for MyNewtype boilerplate, you annotate the type and the macro generates a compile-time-checked implementation.
It covers ten traits — FromPrimitive, ToPrimitive, NumOps, NumCast, Zero, One, Num, Float, Signed, and Unsigned — and understands two common shapes: C-like enums (deriving From/ToPrimitive based on discriminant values) and single-field newtype structs (auto-delegating the trait to the wrapped inner numeric type). It has no runtime footprint since proc-macro crates compile away entirely, and it stays deliberately narrow in scope, doing one job for the num-traits ecosystem rather than trying to be a general derive-macro toolkit.
What You Get
- Derive macros for ten numeric traits: FromPrimitive, ToPrimitive, NumOps, NumCast, Zero, One, Num, Float, Signed, and Unsigned
- Automatic discriminant-based FromPrimitive/ToPrimitive generation for C-like enums
- Newtype delegation — deriving a trait on a single-field tuple struct forwards the implementation to the wrapped inner type
- A
#[num_traits = "..."]attribute to point the macros at a re-exported or renamednum_traitscrate when it’s only a transitive dependency - no_std compatibility (verified in CI against a bare-metal thumbv6m target) so it works in embedded contexts
- Compile-time diagnostics — malformed input produces a proper
compile_error!rather than a panic during your build
Common Use Cases
- Converting between a C-like Rust enum and its underlying integer representation (e.g. FFI enums, protocol opcodes, state machine variants) via FromPrimitive/ToPrimitive
- Making a numeric newtype wrapper (e.g.
struct Meters(f64)) participate in generic numeric code that’s bounded bynum_traits::NumorZero/One - Implementing embedded/no_std numeric types that still need to satisfy the standard
num-traitstrait bounds used by generic math code - Reducing boilerplate in codebases that define many small numeric wrapper types (currency amounts, IDs, physical units) needing consistent trait coverage
Under The Hood
Architecture
num-derive is a single-file proc-macro crate (src/lib.rs, ~1000 lines) exposing ten #[proc_macro_derive(...)] entry points — FromPrimitive, ToPrimitive, NumOps, NumCast, Zero, One, Num, Float, Signed, and Unsigned. Each follows the same template: parse the input via syn into a Data::Enum or Data::Struct, branch on newtype-vs-enum shape using the shared newtype_inner helper, build the implementation with quote!, and return a TokenStream. A shared NumTraits resolver and anon_const_trick helper (a pattern borrowed from serde) wrap generated code in a hygienic anonymous const _: () = { ... } block so an extern crate num_traits as _num_traits alias can be injected regardless of where the derive is used, letting num_traits be a direct or transitive dependency. Because every derive macro depends on this shared import-resolution core, a change to it would affect all ten macros simultaneously; there’s no other coupling between them.
Tech Stack
Built on Rust’s standard proc-macro trio — proc-macro2 for token-stream manipulation, syn v3 for parsing Rust syntax into an AST, and quote for code-generation templating. Dev-dependencies num 0.4 and num-traits 0.2 exist only to exercise the derived implementations in the test suite; the published crate has zero runtime dependencies since proc-macro crates compile away at build time. CI (GitHub Actions) runs the test matrix against rustc 1.71 (MSRV), stable, beta, and nightly, plus a dedicated no_std check against the thumbv6m-none-eabi embedded target via a separate crate under ci/, and enforces cargo fmt --check.
Code Quality
The test suite (tests/) is organized around behavior rather than mirroring source structure: trivial.rs and newtype.rs cover the two core code paths, with edition-specific [[test]] entries (2015/2018) verifying macro hygiene doesn’t regress across Rust editions, and issue-6.rs/issue-9.rs/issue-16.rs are regression tests tied to specific historical bug reports — a healthy pattern for a macro crate where subtle hygiene bugs recur. Parse errors are converted to compile_error! diagnostics via a small parse! macro rather than panicking; the one exception is newtype_inner, which deliberately panic!()s on an unsupported named-field newtype shape (an explicit unimplemented-feature guard, not a swallowed error). No unsafe code is present. rustfmt is CI-enforced; no clippy step is visible in the reviewed workflow.
What Makes It Unique
num-derive doesn’t introduce new numeric semantics — it automates already-established num-traits interfaces. Its specific contributions are the newtype auto-delegation (deriving numeric traits on a single-field wrapper by forwarding to the inner type) and the anonymous-const import-hygiene trick that lets the macros work whether num_traits is a direct or renamed/transitive dependency. Solid, focused engineering applied to a well-understood boilerplate problem rather than a novel technical approach.
Used by 2 apps in this directory
Qdrant
Databases · AI Development · Search
Open-source vector database and search engine built in Rust for production-grade AI applications — from semantic search to RAG pipelines and recommendation systems.
Vaultwarden
Password Manager · Security
Unofficial Bitwarden-compatible server in Rust — run the full Bitwarden ecosystem on a Raspberry Pi using every official client you already have, without the multi-container overhead.