Skip to content

Advanced Struct and Enum Patterns

flowchart TD
    A[Advanced Patterns] --> B[Key Concepts]
    A --> C[Core Principles]
    A --> D[Practical Applications]
    B --> E[Fundamental definitions]
    C --> F[Design patterns]
    D --> G[Real-world usage]

The newtype pattern wraps an existing type in a tuple struct, creating a distinct type with the same Memory representation. This provides type safety without runtime overhead — the compiler eliminates The wrapper after optimization.

struct UserId(u64);
struct OrderId(u64);
fn get_user(id: UserId) -> String {
format!("user_{}", id.0)
}
fn get_order(id: OrderId) -> String {
format!("order_{}", id.0)
}
let uid = UserId(42);
let oid = OrderId(99);
get_user(uid);
get_order(oid);
// get_user(oid); // ERROR: expected UserId, found OrderId

The newtype pattern prevents accidentally passing an OrderId where a UserId is expected. Both Are u64 internally, but the compiler treats them as completely different types.

Newtypes have the same size and alignment as the wrapped type:

struct Millimeters(u32);
struct Meters(u32);
assert_eq!(std::mem::size_of::<Millimeters>(), 4);
assert_eq!(std::mem::size_of::<Meters>(), 4);
assert_eq!(std::mem::align_of::<Millimeters>(), 4);

Implementing Deref and DerefMut allows the newtype to behave like the wrapped type for method Calls and deref coercion:

use std::ops::Deref;
struct Wrapper(Vec<String>);
impl Deref for Wrapper {
type Target = Vec<String>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
let w = Wrapper(vec![String::from("hello")]);
let len = w.len(); // calls Vec::len through deref coercion
assert_eq!(len, 1);