A newtype wraps an existing value in a distinct type so the compiler can distinguish values that share the same representation. Instead of accepting several interchangeable `String` and `i32` arguments, a pay-raise function can require `FirstName`, `LastName`, `Age`, and `Pay`. This prevents accidentally swapping a name with another name or pay with age, while making the API describe the domain directly. section 2.2.3
Listing 2.6 Making mistakes
fn calculate_raise(first_name: String,
_last_name: String,
_age: i32,
current_pay: i32) -> i32 {
if first_name == "Sam" { #1
current_pay + 1000
} else {
current_pay
}
}
fn main() {
let first_raise = calculate_raise(
"Smith".to_string(), #2
"Sam".to_string(),
20,
1000
);
println!("{}", first_raise); #3
let second_raise = calculate_raise(
"Sam".to_string(),
"Smith".to_string(),
1000, #4
20
);
println!("{}", second_raise); #5
}
Identically typed parameters allow calls that compile but put a last name in the first-name position and pay in the age position. section 2.2.3
In Rust, define a wrapper struct with a private inner value and expose a constructor plus focused accessors. A constructor can validate input before creating the value: `FirstName::new` rejects names shorter than two characters. Function signatures then demand the wrappers rather than their underlying representations. section 2.2.3
Listing 2.7 The FirstName newtype
struct FirstName {
value: String,
}
impl FirstName {
pub fn new(name: &str) -> Result<FirstName, String> {
if name.len() < 2 {
Err("Name should be at least two characters".to_string())
} else {
Ok(FirstName {
value: name.to_string(),
})
}
}
pub fn get_value(&self) -> &String {
&self.value
}
}
// code for the other three newtypes
fn calculate_raise(first_name: FirstName, #1
_last_name: LastName,
_age: Age,
current_pay: Pay) -> Pay {
// ...
}
`FirstName` keeps its `String` private, validates construction in `new`, and changes `calculate_raise` to accept domain-specific types. section 2.2.3
A type alias improves readability but does not create a new type. With `type FirstName = String`, callers may still pass any `String` where a first name is expected. Use an alias for a shorter spelling of a complex type or a shared `Result` error type; use a newtype when the type system must enforce the distinction. section 2.2.3
Newtypes are useful when values need domain meaning or controlled construction:
- Separate same-representation fields such as names, ages, and pay in function parameters. section 2.2.3
- Hide a raw value behind a public API that is easier to understand and evolve. section 2.2.3
- Generate repeated accessors or conversion implementations with declarative macros when several wrappers share a shape. section 2.2.3 section 2.4
Reducing accessor boilerplate
Several wrappers may all store a `value` field but need accessors returning different underlying types. A `generate_get_value!` macro can accept a struct identifier and optional return type, using `String` as the default. Keep the macro focused on repeated implementation detail; the wrapper types and their validation rules remain explicit. section 2.2.3



