Ergonomic utilities for the Rust newtype idiom.
This crate avoids the bloat of general-purpose "derive-all" libraries, offering instead a minimalist set of powerful tools specifically for the newtype idiom.
Development is focused on three core pillars:
- Conversions between
newtypesand betweennewtypesand their inner types. - Operations directly on
newtypevalues. - Iteration over ranges of
newtypes.
cargo add newtype-toolsThe simplest way to use the crate is to define a tuple struct using the newtype attribute:
# #[cfg(feature = "derive")]
# {
// Derive `newtype` with `Amount` properties (see below for more details).
#[newtype_tools::newtype(Amount)]
// More traits can be easily derived:
#[derive(serde::Serialize)]
struct Apples(u64);
// The `newtype` can also be easily extended:
impl core::fmt::Display for Apples {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.0, f)
}
}
// Now the `Apples`behave pretty much as their inner type `u64`:
let apple1 = Apples(2);
// `Apples` can be converted from the inner type:
let apple2 = Apples::from(3);
// `Apples` can be added, subtracted and compared:
assert_eq!(apple1 + apple2, Apples(5));
// `Apples` can be multiplied by the inner factor:
assert_eq!(apple1 * 2_u64, Apples(4));
// `Apples` can be divided, returning a inner ratio:
assert_eq!(apple2 / apple1 , 1);
# }The crate supports two main "presets": Amount and Id. See below for more details.
Instead of using predefined sets, the implementation allows for the derivation of only the specific traits required for a given use case.
newtype conversions:
# #[cfg(feature = "derive")]
# {
use newtype_tools::Newtype;
#[derive(Newtype)]
#[newtype(
into(Oranges, with = |apples| Oranges((apples.0 / 2) as u32))
)]
struct Apples(u64);
struct Oranges(u32);
let apples = Apples(42);
// `Oranges` can now be created from `Apples`:
let oranges = Oranges::from(apples);
assert_eq!(oranges.0, 21);
# }newtype operations:
# #[cfg(feature = "derive")]
# {
use newtype_tools::Newtype;
#[derive(Debug, Newtype)]
#[newtype(
partial_eq(Oranges, with = |apples, oranges| apples.0 == oranges.0 as u64 * 2)
)]
struct Apples(u64);
struct Oranges(u32);
let apples = Apples(42);
let oranges = Oranges(21);
// `Apples` and `Oranges` can now be compared:
assert!(apples == oranges);
# }newtype range iteration:
# #[cfg(feature = "derive")]
# {
use newtype_tools::{Newtype, Iter};
#[derive(Debug, Newtype)]
struct Apples(u64);
let range = Apples(0)..Apples(42);
// The range of `Apples` can now be iterated:
for apple in range.iter() {
println!("{apple:?}");
}
# }Note: This will become even more ergonomic once the Rust Step trait is stabilized.
The crate supports predefined sets of newtype properties. The concept is similar
to the phantom_newtype crate but avoids Rust orphan rule limitations by defining
the newtype locally. This allows additional traits to be implemented easily
and makes the set of derived traits simple to extend.
The supported newtype kinds are:
| Trait | #[newtype(Amount)] |
#[newtype(Id)] |
|---|---|---|
Clone |
✔ | ✔ |
Copy |
✔ | ✔ |
Debug |
✔ | ✔ |
Default |
✔ | ✔ |
Eq¹ |
✔ | ✔ |
Hash¹ |
✔ | ✔ |
Ord¹ |
✔ | ✔ |
PartialEq |
✔ | ✔ |
PartialOrd |
✔ | ✔ |
From<Repr> |
✔ | ✔ |
Add<Self> |
✔ | ✘ |
AddAssign<Self> |
✔ | ✘ |
Sub<Self> |
✔ | ✘ |
SubAssign<Self> |
✔ | ✘ |
Mul<Repr> |
✔ | ✘ |
MulAssign<Repr> |
✔ | ✘ |
Div<Self> |
✔ | ✘ |
Eq,OrdandHashare only implemented for integer inner types.
-
derive_more-- A large library (~10K SLoC) focused on supporting a wide range of traits for any data type. It can be used alongsidenewtype-toolsto derive additional traits as needed: crate | repo.The unique value of this
newtype-toolscrate lies in its lightweight design, support for property sets like#[newtype(Amount)], and built-in range iteration via(Newtype(0)..Newtype(42)).iter(). -
nutype-- A comprehensive library (7.5K SLoC) primarily focused onnewtypevalidation: crate | repo. -
phantom_newtype-- Provides 19 trait implementations out of the box, but lacks a mechanism for custom trait implementations due to the Rust orphan rule: crate | repo. -
newtype_derive-- Legacy crate that relies on thecustom_derive!declarative macro: crate | repo. -
newtype-derive-2018-- Less outdated, but still based on the declarative macro: crate | repo. -
newtype-- An older crate following a similar approach but no longer actively maintained: crate | repo.
- Rust newtype idiom.
- Rust Step trait.