Course contentsShow
← Rust
Lesson 491 of 1,9105. Type System and TraitsFree lesson

Newtype Pattern for Type Safety

Prevent domain-logic errors by wrapping primitive values in type-safe containers.

In this lesson, you will learn how to use the Newtype pattern to prevent logic errors by ensuring your code cannot accidentally mix up different units of measurement or identifiers.

The Problem with Primitives

In many systems, we represent various concepts using basic types like u32 or String. For instance, imagine a function that calculates a delivery fee based on weight and distance:

fn calculate_fee(weight: u32, distance: u32) -> u32 {
    weight * distance
}

If you accidentally call calculate_fee(distance, weight), the compiler won't stop you because both are just u32. This "primitive obsession" is a frequent source of bugs in domain logic.

The Newtype Solution

The Newtype pattern solves this by wrapping the primitive in a tuple struct. By creating a unique type, you make it impossible for the compiler to treat them as interchangeable.

struct Weight(u32);
struct Distance(u32);

fn calculate_fee(weight: Weight, distance: Distance) -> u32 {
    weight.0 * distance.0
}

fn main() {
    let w = Weight(10);
    let d = Distance(50);

    // This works fine:
    calculate_fee(w, d);

    // This would cause a compile-time error:
    // calculate_fee(d, w); 
}

Why it Matters

When you wrap your primitives, you gain "nominal typing" benefits. Even though Weight and Distance are both backed by a u32, the compiler treats them as entirely distinct types.

You aren't just adding a layer of safety; you are creating documentation for your types. When you read a function signature, calculate_fee(Weight, Distance) is significantly more self-explanatory than calculate_fee(u32, u32).

Because these are single-element tuple structs, they have zero runtime overhead compared to using the raw primitive. The compiler sees through the wrapper during optimization, but enforces the boundaries while you write your code. This allows you to catch domain-logic errors at compile time rather than tracking them down in production.

Key Takeaway: The Newtype pattern enforces domain-specific rules by treating identical primitive types as distinct, incompatible types to prevent accidental misuse.