patternrustTip
Newtype pattern: type-safe wrappers around primitives
Viewed 0 times
newtypewrappertype safetyzero-costprimitivetuple structorphan rule
Error Messages
Problem
Functions accept raw primitive types (u64, String) that represent different semantic concepts, making it easy to pass arguments in the wrong order or confuse units.
Solution
Wrap primitives in single-field tuple structs to create distinct types:
// Without newtype: easy to confuse user_id and post_id
fn get_post(user_id: u64, post_id: u64) -> String { todo!() }
// get_post(post_id, user_id) — wrong order, compiles silently
// With newtype: compiler catches the mistake
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UserId(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PostId(u64);
impl UserId {
pub fn new(id: u64) -> Self { UserId(id) }
pub fn value(self) -> u64 { self.0 }
}
fn get_post(user_id: UserId, post_id: PostId) -> String { todo!() }
// get_post(PostId(1), UserId(2)) — compile error!
// Newtype for units
#[derive(Debug, Clone, Copy)]
pub struct Meters(f64);
#[derive(Debug, Clone, Copy)]
pub struct Kilograms(f64);
// Cannot accidentally add meters to kilogramsWhy
Newtype wrappers are zero-cost — the compiler optimizes them away and the struct has identical memory layout to its inner type. They provide type safety without runtime overhead.
Gotchas
- You must implement Display, Add, etc. explicitly — the inner type's trait implementations don't automatically apply
- Use impl Deref for a newtype that wraps a collection to forward slice methods automatically
- The newtype pattern also enables implementing foreign traits on foreign types (the orphan rule)
Revisions (0)
No revisions yet.