Type System Fundamentals
Primitive and Composite Types
Every type in Rust is either a primitive type built directly into the language compiler or a composite type constructed by combining existing types.
Taxonomy of Rust's Type Hierarchy
Rust Types
├── 1. Primitive Types (Built into the compiler)
│ ├── Scalars: i8..i128, isize, u8..u128, usize, f32, f64, bool, char
│ ├── Text slice: str (unsized UTF-8 slice)
│ ├── Unit & !: () (1 value) and ! (never type, 0 values)
│ └── Pointers: &T, &mut T, *const T, *mut T, fn(...) -> R
│
└── 2. Composite Types (Built from other types)
├── Structural: Tuples `(A, B)`, Arrays `[T; N]`, Slices `[T]`
└── Nominal: `struct`, `enum`, `union`, Trait Objects `dyn Trait`
Reference of Built-In Primitive Types
| Category | Types | Size in Memory | Notes |
|---|---|---|---|
| Signed Integers | i8, i16, i32, i64, i128, isize |
1, 2, 4, 8, 16 B, pointer-sized | Two's complement representation |
| Unsigned Integers | u8, u16, u32, u64, u128, usize |
1, 2, 4, 8, 16 B, pointer-sized | usize is used for all slice indices and memory sizes |
| Floating-Point | f32, f64 |
4 B, 8 B | IEEE 754 single and double precision |
| Boolean | bool |
1 B (0x00 or 0x01) |
Niche-optimized (0x02..=0xFF unused) |
| Unicode Scalar | char |
4 B | Valid Unicode scalar value (0x0000..=0xD7FF, 0xE000..=0x10FFFF) |
| Unit Type | () |
0 B (ZST) |
Returned by expressions with no meaningful value |
| Never Type | ! |
0 B (uninhabited) |
Returned by diverging expressions (panic!, return, loop {}) |
Structural Composite Types: Tuples, Arrays, and Slices
fn analyze_packet(packet: &[u8; 4]) -> (u8, &[u8]) {
let version = packet[0];
let payload: &[u8] = &packet[1..];
(version, payload)
}
fn main() {
let raw: [u8; 4] = [1, 0xAA, 0xBB, 0xCC];
let (ver, body) = analyze_packet(&raw);
println!("Version: {ver}, body: {body:02X?}");
}
Version: 1, body: [AA, BB, CC]
Even though primitive types (i32, str, [T]) are built into the compiler for layout and code generation, the standard library (core and std) attaches inherent methods and trait implementations directly to them (impl i32 { ... }, impl<T> [T] { ... }) so primitives and composite types share a unified method-call syntax (42_u32.count_ones(), [1, 2, 3].len()).