Skip to main content

Crate newtypes

Crate newtypes 

Source
Expand description

§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::*;
use serde::{Deserialize, Serialize};

newtype!(NewTypeName, u32);

// In case we want to automatically derive traits (Note that we can
// add as many traits as we need after the `;` character):
newtype!(AnotherTypeName, u32; Deserialize, Serialize);

Example:

use newtypes::*;
use serde::{Deserialize, Serialize};

newtype!(UserId, u32);

newtype!(GroupId, u32; Serialize, Deserialize);

§Implemented traits

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 the From and FromStr traits for us.

Otherwise we can choose to implement From, FromStr and TryFrom manually.

§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::*;
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::*;
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::*;

#[derive(Debug, Clone, PartialEq)]
pub struct Vec3 { pub x: f32, pub y: f32, pub z: f32 }

newtype_struct!(Vertex, Vec3);

§newtype_array! Macro

The newtype_array! macro creates a newtype that wraps a fixed-size array [T; N] and exposes array-like ergonomics: indexing (single element and ranges), iteration in all three forms (Self, &Self, &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. Use newtype_array_deref! when full transparent-slice access is desired.

Default trait derives depend on the element type: integers get Copy + Eq + Hash, floats get Copy + PartialEq, String gets Eq + Hash (no Copy), arbitrary types get the conservative Debug + Clone + PartialEq baseline. Extra derives can be requested after ; like every other macro in this crate.

Example:

use newtypes::*;

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::*;
use serde::{Deserialize, Serialize};

newtype_array!(MacAddr, u8, 6; Serialize, Deserialize);

From<[T; N]> is not emitted by default. Combine with newtype_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.

Example:

use newtypes::*;

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. 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.

Example:

use newtypes::*;

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());

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! with newtype_from! (or newtype_from_only!) on the same newtype emits From<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!(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!(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::*;
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 implement FromStr manually.

Macros§

newtype
The newtype! macro creates a barebones newtype by wrapping an inner type with a struct, and, if possible, implementing some extra traits for it.
newtype_array
The newtype_array! macro creates a newtype that wraps a fixed-size array [T; N] and exposes array-like ergonomics: indexing (single element and ranges), iteration in all three forms (Self, &Self, &mut Self), inherent len, is_empty, iter, iter_mut, as_array, as_array_mut, plus AsRef<[T]>, AsMut<[T]>, and Into<[T; N]>.
newtype_array_deref
The newtype_array_deref! macro behaves exactly like newtype_array! but additionally implements Deref<Target = [T]> and DerefMut.
newtype_from
The macro newtype_from! implements the From and FromStr traits for us in case it’s possible.
newtype_from_only
The macro newtype_from_only! implements only the From trait for a newtype, leaving FromStr to the caller. Use it when a type should be constructible from its inner value but should not parse from text.
newtype_fromstr
The macro newtype_fromstr! implements only the FromStr trait for a newtype, 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.
newtype_ord
The newtype_ord! macro extends the functionality provided with newtype! by implementing the PartialOrd and Ord traits (when possible).
newtype_struct
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>.
newtype_unit
The newtype_unit! macro extends the functionality provided with newtype_ord! by implementing the Add, Sub, AddAssign, SubAssign, and Default traits.
newtype_validated
The newtype_validated! macro creates a newtype whose constructor enforces an invariant. Validation always runs; there is no unsafe escape hatch. Invalid values yield Err(<ErrorName>).