Type System Fundamentals
Type Aliases
A type alias (type AliasName = ExistingType;) defines a synonym for an existing type to shorten complex type signatures without creating a distinct nominal type.
Type Alias vs. Newtype Struct
The critical property of a type alias is that the compiler treats the alias and the underlying type as 100% identical and interchangeable:
| Feature | Type Alias (type Kilometers = i32;) |
Newtype (struct Kilometers(i32);) |
|---|---|---|
| Creates a distinct type? | No (Kilometers and i32 are the exact same type) |
Yes (nominal type checked by compiler) |
Prevents mixing with i32 (E0308)? |
No (let x: i32 = km; compiles silently) |
Yes |
Can implement traits directly (impl Trait for Alias)? |
No (would implement on the underlying type) | Yes (bypasses orphan rules) |
| Primary purpose | Readability and reducing repetitive generic boilerplate | Type safety, encapsulation, custom trait impls |
type Kilometers = i32;
type Miles = i32;
fn main() {
let dist_km: Kilometers = 100;
let dist_mi: Miles = 62;
// Because both are aliases for `i32`, adding them compiles without error!
let mixed: i32 = dist_km + dist_mi;
println!("Mixed sum (no type separation): {mixed}");
}
Mixed sum (no type separation): 162
Generic Type Aliases (std::io::Result<T>)
Type aliases shine when they partially apply a generic type parameter that stays constant across an entire module or crate. For example, std::io defines:
pub type Result<T> = core::result::Result<T, std::io::Error>;
You can use the same pattern in your own modules to fix an error type, allocator, or hasher:
use std::collections::HashMap;
#[derive(Debug)]
pub enum DbError {
ConnectionLost,
NotFound(u64),
}
// Partially applied Result alias fixing the error type to `DbError`:
pub type DbResult<T> = Result<T, DbError>;
// Complex nested callback/collection alias:
pub type UserCache<V> = HashMap<u64, V>;
fn find_username(cache: &UserCache<String>, id: u64) -> DbResult<&str> {
cache
.get(&id)
.map(String::as_str)
.ok_or(DbError::NotFound(id))
}
fn main() {
let cache = UserCache::from([(1_u64, String::from("alice"))]);
println!("User 1: {:?}", find_username(&cache, 1));
println!("User 9: {:?}", find_username(&cache, 9));
}
User 1: Ok("alice")
User 9: Err(NotFound(9))