Type Aliases (type) / Newtype Pattern
| Since: | Rust 1.0(2015) |
|---|
In Rust, you can use the type keyword to create an alias for an existing type, or use the newtype pattern — wrapping a type in a tuple struct — to improve type safety.
Syntax
type Kilometers = i32;
type Result<T> = std::result::Result<T, String>;
type Callback = Box<dyn Fn(i32) -> i32>;
// Newtype pattern — wraps a type in a tuple struct.
struct Meters(f64);
struct Kilograms(f64);
// Implement From / Display for the newtype.
impl From<f64> for Meters {
fn from(val: f64) -> Self { Meters(val) }
}
// Extract the inner value from the newtype.
let m = Meters(5.0);
let val: f64 = m.0; // Field access on a tuple struct
Concepts
| Feature | Description |
|---|---|
| type alias | An alternative name for an existing type. Type checking treats it identically to the original type. Useful for shortening long type names. |
| Newtype pattern | Wraps a type in a tuple struct. Improves type safety by turning incorrect assignments into compile errors. |
| Newtype + Deref | Implementing the Deref trait lets you transparently call methods of the inner type. |
| type T = ... (generics) | You can create an alias that fixes some type parameters of a generic type (partially applied alias). |
| Preventing unit mixing | Making Meters and Kilometers distinct types prevents bugs caused by accidentally mixing units. |
Sample Code
sample_type_alias_newtype.rs
use std::fmt;
use std::ops::Add;
// Type alias examples
type Thunk = Box<dyn Fn() -> String>;
type Point2D = (f64, f64);
type StrResult<T> = Result<T, String>;
// Newtype pattern — represent units as distinct types.
#[derive(Debug, Clone, Copy, PartialEq)]
struct Meters(f64);
#[derive(Debug, Clone, Copy, PartialEq)]
struct Kilograms(f64);
impl Meters {
fn new(val: f64) -> Self { Meters(val) }
fn value(&self) -> f64 { self.0 }
}
// Implement the Add trait.
impl Add for Meters {
type Output = Meters;
fn add(self, other: Meters) -> Meters {
Meters(self.0 + other.0)
}
}
// Implement the Display trait.
impl fmt::Display for Meters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}m", self.0)
}
}
// Implement the From trait.
impl From<f64> for Meters {
fn from(val: f64) -> Self { Meters(val) }
}
fn total_distance(a: Meters, b: Meters) -> Meters {
a + b
}
fn main() {
// Type aliases
let point: Point2D = (3.0, 4.0);
println!("point: {:?}", point);
let greet: Thunk = Box::new(|| String::from("Hello!"));
println!("{}", greet());
// Using StrResult.
let ok: StrResult<i32> = Ok(42);
let err: StrResult<i32> = Err(String::from("error"));
println!("ok: {:?}, err: {:?}", ok, err);
// Using the newtype
let dist1 = Meters::new(100.0);
let dist2 = Meters::new(250.5);
let total = total_distance(dist1, dist2);
println!("Total distance: {}", total); // 350.5m
// The newtype prevents accidental assignments.
let weight = Kilograms(75.0);
// total_distance(dist1, weight); // Compile error! Mismatched types.
println!("Weight: {:?}", weight);
// Accessing the inner value
println!("dist1 value: {}", dist1.value()); // 100
// Newtype + From trait
let m: Meters = 50.0_f64.into();
println!("into(): {}", m); // 50m
}
Compile with the following command:
rustc type_alias_newtype.rs
./type_alias_newtype
point: (3.0, 4.0)
Hello!
ok: Ok(42), err: Err("Error")
Total distance: 350.5m
Weight: Kilograms(75.0)
dist1 value: 100
into(): 50m
Common Mistakes
Mistake 1: Assuming a type alias provides type safety
A type alias (type) is just another name for the same underlying type. Mixing up units is not detected by the type checker. Use the newtype pattern when type safety is needed.
type_alias_mistake1.rs
type Meters = f64;
type Kilograms = f64;
fn calculate_bmi(weight: Kilograms, height: Meters) -> f64 {
weight / (height * height)
}
fn main() {
let height: Meters = 1.75;
let weight: Kilograms = 70.0;
// Swapping the arguments does not cause a compile error (both are f64).
let bmi = calculate_bmi(height, weight); // Wrong argument order
println!("BMI: {:.2}", bmi); // Incorrect value is printed.
}
Compile with the following command:
rustc type_alias_mistake1.rs ./type_alias_mistake1 BMI: 0.00
With the newtype pattern, swapping the arguments causes a compile error.
type_alias_fix1.rs
#[derive(Debug, Clone, Copy)]
struct Meters(f64);
#[derive(Debug, Clone, Copy)]
struct Kilograms(f64);
fn calculate_bmi(weight: Kilograms, height: Meters) -> f64 {
weight.0 / (height.0 * height.0)
}
fn main() {
let height = Meters(1.75);
let weight = Kilograms(70.0);
// Swapping the arguments causes a compile error.
// calculate_bmi(height, weight); // error: mismatched types
let bmi = calculate_bmi(weight, height);
println!("BMI: {:.2}", bmi); // 22.86
}
Compile with the following command:
rustc type_alias_fix1.rs ./type_alias_fix1 BMI: 22.86
Mistake 2: Forgetting .0 when accessing the inner value of a newtype
To access the inner value of a newtype tuple struct, use .0. Trying to use the struct directly in an arithmetic expression causes a compile error.
type_alias_mistake2.rs
struct Meters(f64);
fn main() {
let m = Meters(100.0);
// Using m directly in arithmetic causes a compile error.
// let doubled = m * 2.0; // Compile error
// Use .0 to extract the inner value.
let doubled = m.0 * 2.0;
println!("doubled: {}", doubled); // 200
// Alternatively, implement Add to enable arithmetic operators.
// (See the Add implementation in sample_type_alias_newtype.rs)
}
Compile with the following command:
rustc type_alias_mistake2.rs ./type_alias_mistake2 doubled: 200
Overview
A type alias (type) is used to shorten long type names. Because type checking treats the alias identically to the original type, it does not improve type safety — it is purely a readability convenience.
The newtype pattern wraps a type in a tuple struct to prevent type mixing at compile time. For example, Meters and Kilograms are treated as distinct types, so passing the wrong one as an argument becomes a compile error. The newtype pattern is also useful for implementing additional traits on types from external crates (working around the orphan rule).
For implementing conversion traits, see From / Into / TryFrom / TryInto Traits. For numeric type casting, see as Cast / Numeric Type Conversion.
If you find any errors or copyright issues, please contact us.