Newtype pattern for type-safe primitives

Marcus Chen Jan 2026
1 tab
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct UserId(u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct PostId(u32);

fn fetch_user(id: UserId) {
    println!("Fetching user {:?}", id);
}

fn main() {
    let user_id = UserId(42);
    fetch_user(user_id);
    // fetch_user(PostId(42)); // ❌ compile error
}
1 file · rust Explain with highlit

The newtype pattern wraps a primitive in a tuple struct to create a distinct type. This prevents mixing up values that are semantically different but have the same underlying type (like UserId(u32) vs PostId(u32)). The compiler enforces that you can't pass a UserId where a PostId is expected. Derive Debug, Clone, Copy, etc., as needed. For conversions, implement From and Into. I use newtypes extensively in domain modeling to encode business rules in the type system. The cost is zero: the wrapper is optimized away at runtime. This pattern makes APIs safer and self-documenting, and it catches bugs at compile time that would be runtime errors in dynamic languages.