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))