nonmax

Rust integer types that can hold any value except their type's maximum, so Option<T> costs nothing extra over T.

Library
Cargo
v0.5.5
64stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity24
Maintenance4
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture85
Code Quality90
Innovation55
Learning Curve65

nonmax gives Rust code a family of integer newtypes — NonMaxI8 through NonMaxI128 and NonMaxU8 through NonMaxU128, plus isize/usize variants — that mirror the standard library’s NonZero* types but forbid the maximum representable value instead of zero. Internally each type stores its value XORed against the primitive’s MAX and wraps it in core::num::NonZero, so the compiler’s niche-value optimization applies for free: size_of::<Option<NonMaxU32>>() is identical to size_of::<NonMaxU32>().

The crate is a single, dependency-free module (serde support is optional and feature-gated) that is #![no_std] by default, ships const constructors and const ZERO/ONE/MAX associated constants, and implements the same trait surface as std’s NonZero* family — Default, TryFrom, FromStr, Ord/PartialOrd, BitAnd[Assign], and the full Debug/Display/Binary/Octal/LowerHex/UpperHex set — so it drops into existing code with familiar ergonomics. It’s a natural fit anywhere a sentinel-max convention (rather than sentinel-zero) is used to mark an invalid index, handle, or slot.

What You Get

  • NonMax variants for all 12 integer primitives (i8-i128, isize, u8-u128, usize) matching std’s NonZero* naming convention
  • Guaranteed niche optimization: Option<NonMaxT> is exactly the same size as NonMaxT
  • const fn constructors (new, new_unchecked, get) plus const ZERO/ONE/MAX associated constants
  • Full std trait parity: Default, TryFrom, FromStr, Ord/PartialOrd, BitAnd[Assign], and Debug/Display/Binary/Octal/LowerHex/UpperHex formatting
  • Optional serde feature implementing Serialize/Deserialize with the max-exclusion invariant enforced on deserialize
  • #![no_std] by default (the std feature is additive, only needed for std::error::Error impls)

Common Use Cases

  • Packing entity/slot indices in game engines or ECS-style data structures so Option<Index> costs nothing extra over Index
  • Serializing indices or handles with serde while validating they never deserialize as the reserved sentinel max value
  • no_std or embedded Rust code that needs niche-optimized ‘invalid’ sentinels without pulling in std
  • Porting codebases that use max-value-as-invalid conventions onto a type-safe wrapper that mirrors the familiar NonZero* API

Under The Hood

Architecture nonmax is a single-file crate (src/lib.rs, ~580 lines) built around one core type, NonMax<Integer: HasNonZero>, which wraps Integer::NonZero via a private HasNonZero trait mapping each primitive to core::num::NonZero<T>. A nonmax! macro is invoked once per primitive to generate that primitive’s constructors, trait implementations, and an inline #[cfg(test)] module, avoiding hand-duplicated boilerplate across twelve near-identical types at the cost of the implementation being macro-expanded rather than directly readable. The crate has no runtime dependencies of its own; the only dependency is an optional serde integration gated behind a feature flag. Because every generated type routes through the same macro body, changing the core XOR-encoding trick in new/get would affect all twelve types simultaneously.

Tech Stack Pure Rust, edition 2018, MSRV 1.83.0, with no build tooling beyond cargo. The sole optional runtime dependency is serde ^1.0 (default-features = false) behind a serde feature; bincode ^1.3 is a dev-dependency used to exercise round-trip (de)serialization in tests. The crate is #![no_std] by default, with a std feature that only adds std::error::Error impls for its two error types. GitHub Actions CI runs a matrix across stable, MSRV, all-features, and no_std configurations, plus a separate rustfmt + clippy lint job.

Code Quality Each primitive’s macro expansion includes an inline test module covering construction, the size-equality proof that the niche optimization holds, TryFrom/FromStr conversions, formatting, ordering, and bitwise-and behavior, so all twelve numeric types get equivalent coverage without hand-written duplication. TryFromIntError and ParseIntError are implemented manually to mirror std’s error types, including std::error::Error impls gated behind the std feature. The two unsafe functions in the crate (new_unchecked and the HasNonZero trait impl) carry explicit safety comments explaining their invariants. Naming and trait coverage deliberately track std’s NonZero* family for discoverability, and CI enforces both rustfmt and clippy on every push.

API Design The public API is intentionally minimal and mirrors std’s NonZero* types almost method-for-method, so anyone familiar with NonZeroU32 can pick up NonMaxU32 with no new mental model. Constructors are const, associated ZERO/ONE/MAX constants remove common boilerplate, and the crate’s single README doctest doubles as its onboarding example. Documentation coverage is enforced via #![forbid(missing_docs)], so every public item has doc comments, though there is no separate examples/ directory or CONTRIBUTING guide beyond the README and CHANGELOG.

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