- Rust 100%
|
|
||
|---|---|---|
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE.md | ||
| README.md | ||
Newtypes
The newtypes crate is meant to ease the implementation of the
Newtype
pattern while trying to make as few assumptions as possible.
Layout guarantee
Every type produced by every macro in this crate is annotated with
#[repr(transparent)],
so its size and alignment match the inner type's. This is a stable part of
the crate's contract and will not be removed.
newtype! Macro
The newtype! macro creates a barebones newtype by wrapping an inner type with
a struct, and, if possible, implementing some extra traits for it.
By default, all declared structs have visibility pub.
use newtypes::newtype;
newtype!(NewTypeName, underlying_type);
// In case we want to automatically derive traits (like serde's
// Serialize or Deserialize):
newtype!(NewTypeName, underlying_type; Trait1, ..., TraitN);
Example:
use newtypes::newtype;
use serde::{Deserialize, Serialize}
newtype!(UserId, u32);
newtype!(GroupId, u32; Serialize, Deserialize);
Implemented traits
- For all:
- For integers:
- For floating point numbers:
- For
String:
IMPORTANT: The inner value is private. If there are no constraints on the inner value, then we can rely on
newtype_from!macro to implement theFromandFromStrtraits for us.Otherwise we can choose to implement
From,FromStrandTryFrommanually.
newtype_ord! Macro
The newtype_ord! macro extends the functionality provided with
newtype! by implementing the
PartialOrd and
Ord traits
(when possible).
Example:
use newtypes::newtype_ord;
use serde::{Deserialize, Serialize}
newtype_ord!(Rank, u16);
newtype_ord!(TicketNumber, u16; Serialize, Deserialize);
NOTE: It only works for integers, floating point numbers, and
String.
newtype_unit! Macro
The newtype_unit! macro extends the functionality provided with
newtype_ord! by implementing the
Add,
Sub,
AddAssign,
SubAssign, and
Default traits.
Example:
use newtypes::newtype_unit;
use serde::Deserialize;
newtype_unit!(Weight, f32);
newtype_unit!(Length, f32; Deserialize);
NOTE: It only works for integers and floating point numbers.
NOTE: It does not implement arithmetic operations beyond addition and subtraction. Doing that properly would require a more complex library focused on dealing with "units" (example: multiplying lengths gives us an area).
newtype_struct! Macro
The newtype_struct! macro creates a newtype that wraps a user-defined struct
(or any non-scalar inner type), implementing
Debug,
Clone,
PartialEq, and
Into<inner_type>.
The inner type must itself implement Debug + Clone + PartialEq. Extra traits
(Copy, Hash, Eq, Ord, PartialOrd, Serialize, …) can be requested by
passing them after the ; separator and will be derived on the generated
struct.
Example:
use newtypes::newtype_struct;
#[derive(Debug, Clone, PartialEq)]
pub struct Vec3 { pub x: f32, pub y: f32, pub z: f32 }
newtype_struct!(Vertex, Vec3);
With extra derives:
use newtypes::newtype_struct;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Vec3 { pub x: f32, pub y: f32, pub z: f32 }
newtype_struct!(Vertex, Vec3; Serialize, Deserialize);
newtype_array! Macro
The newtype_array! macro creates a newtype that wraps a fixed-size array
[T; N] and exposes array-like ergonomics:
Index /
IndexMut (covering
usize and every range kind via
SliceIndex<[T]>),
IntoIterator
for Self, &Self, and &mut Self, inherent len, is_empty, iter,
iter_mut, as_array, as_array_mut, plus AsRef<[T]>, AsMut<[T]>, and
Into<[T; N]>.
Deref<Target = [T]> is not emitted by default — see
newtype_array_deref! for the variant that adds
it.
Default trait derives depend on the element type:
- Integers (
i8..i128,u8..u128,isize,usize):Debug, Clone, Copy, PartialEq, Eq, Hash - Floats (
f32,f64):Debug, Clone, Copy, PartialEq String:Debug, Clone, PartialEq, Eq, Hash- Any other element type:
Debug, Clone, PartialEq(the inner type must itself implement these)
Extra derives can be requested after ;:
use newtypes::newtype_array;
newtype_array!(MacAddr, u8, 6);
let m = MacAddr([0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe]);
assert_eq!(0xde, m[0]);
assert_eq!(6, m.len());
let _slice: &[u8] = &m[1..3];
With extra derives:
use newtypes::newtype_array;
use serde::{Deserialize, Serialize};
newtype_array!(MacAddr, u8, 6; Serialize, Deserialize);
NOTE:
From<[T; N]>is not emitted by default. Pair withnewtype_from_only!if implicit construction from an array is desired.
newtype_array_deref! Macro
The newtype_array_deref! macro behaves exactly like
newtype_array! but additionally implements
Deref<Target = [T]>
and
DerefMut.
This exposes every slice method (first, last, contains, fill,
chunks, windows, split, …) directly on the newtype and lets &Name
coerce to &[T] implicitly. The convenience comes at the cost of partially
erasing the wrapper at call sites, which can defeat the type-safety
motivation of the newtype pattern. Prefer
newtype_array! unless you specifically want the
full slice surface.
use newtypes::newtype_array_deref;
newtype_array_deref!(Buf, u8, 4);
let b = Buf([1, 2, 3, 4]);
assert_eq!(Some(&1u8), b.first());
assert_eq!(Some(&4u8), b.last());
assert!(b.contains(&3));
newtype_validated! Macro
The newtype_validated! macro creates a newtype whose constructor enforces an
invariant. Validation always runs; there is no unsafe escape hatch. Invalid
values yield an error.
The error type is generated alongside the newtype as a zero-sized struct
implementing Debug + Clone + Copy + PartialEq + Eq + Display + Error. Its
name must be supplied explicitly because macro_rules! cannot synthesise
{Name}Error on stable Rust without third-party dependencies.
From<inner> is not emitted (it would bypass validation).
TryFrom<inner> is
emitted and delegates to Self::new.
The validator can be either a closure or a free-function path; both must be
callable as Fn(&inner) -> bool.
Example:
use newtypes::newtype_validated;
newtype_validated!(Probability, ProbabilityError, f32, |x: &f32| (0.0..=1.0).contains(x));
assert!(Probability::new(0.5).is_ok());
assert!(Probability::new(2.0).is_err());
With a free function:
use newtypes::newtype_validated;
fn is_even(x: &u32) -> bool { x % 2 == 0 }
newtype_validated!(EvenU32, EvenU32Error, u32, is_even);
WARNING: Validation runs only at construction time. Wrapping a type with interior mutability (e.g.
RefCell,Cell) defeats the invariant because the inner value can mutate after construction.
WARNING: Combining
newtype_validated!withnewtype_from!(ornewtype_from_only!) on the same newtype emitsFrom<inner>and silently bypasses validation.
newtype_from! Macro
The macro newtype_from! implements the From and FromStr traits for us
in case it's possible.
It has to be used in combination with one of the other ones
(newtype!, newtype_ord!, or
newtype_unit!).
Example:
use newtypes::{newtype, newtype_from};
newtype!(Weight, f32);
newtype_from!(Weight, f32);
NOTE: It only works for integers, floating point numbers, and
String.
newtype_from_only! Macro
The macro newtype_from_only! implements only the From trait, leaving
FromStr to the caller. Use it when a type should be constructible from its
inner value but should not parse from text.
Example:
use newtypes::{newtype, newtype_from_only};
newtype!(VertexIndex, u32);
newtype_from_only!(VertexIndex, u32);
NOTE: It works for integers, floating point numbers,
String, and arbitrary inner types.
newtype_fromstr! Macro
The macro newtype_fromstr! implements only the FromStr trait, leaving
From<inner> to the caller. Use it when a type should parse from text but
should not be implicitly constructible from its inner value.
Example:
use newtypes::{newtype, newtype_fromstr};
use std::str::FromStr;
newtype!(Age, u8);
newtype_fromstr!(Age, u8);
let a = Age::from_str("42").unwrap();
NOTE: It only works for integers, floating point numbers, and
String. For arbitrary inner types implementFromStrmanually.