Primitive Types, Compound Types & Representation Mechanics
Explore scalar types, hardware alignment formulas, struct padding, slices, and fat pointer representation in memory.
Every computational value in Rust corresponds to a concrete, bit-level memory layout. Unlike dynamically typed languages where numbers are wrapped in runtime boxed objects, Rust scalar primitives represent bare hardware representations with deterministic bit widths and hardware alignment constraints.
Slice references consist of an 8-byte base address and an 8-byte length indicator.
1. Scalar Primitives#
Scalar primitives represent single values stored directly on the stack or in CPU registers:
| Category | Types | Memory Width | Representation |
|---|---|---|---|
| Signed Integers | i8, i16, i32, i64, i128 | bytes | Two's complement representation. |
| Unsigned Integers | u8, u16, u32, u64, u128 | bytes | Direct binary magnitude. |
| Pointer Sized | isize, usize | 4 or 8 bytes (Target arch) | Sized to match platform address space. |
| Floating Point | f32, f64 | 4 or 8 bytes | Standard IEEE 754 precision format. |
| Boolean | bool | 1 byte | 0x00 (false) or 0x01 (true). |
| Character | char | 4 bytes | Unicode Scalar Value (valid UTF-32 range). |
cargo run), integer overflow triggers an explicit runtime panic assertion. In release mode (cargo build --release), rustc compiles arithmetic with two's complement wrapping to maximize CPU pipeline throughput. Use explicit checked methods like .checked_add() or .saturating_add() when overflow is anticipated.2. Compound Types: Tuples & Arrays#
Compound types bundle multiple values together into a single composite type:
12345678910111213use std::mem::{size_of, align_of}; fn main() { // 1. Fixed-size Array (homogeneous sequence, stack allocated) let numbers: [i32; 4] = [10, 20, 30, 40]; // 2. Heterogeneous Tuple (multiple types grouped) let record: (u8, u64, u16) = (1, 1000, 42); println!("Array size: {} bytes", size_of::<[i32; 4]>()); // 16 bytes println!("Tuple size: {} bytes", size_of::<(u8, u64, u16)>()); // 16 bytes due to padding! println!("Tuple alignment: {} bytes", align_of::<(u8, u64, u16)>()); // 8 bytes }
3. Mathematical Foundations: Alignment & Padding#
Every type in Rust has an alignment requirement , which is a power of two (). The CPU hardware mandates that an instance of type must be stored at a memory address that is a multiple of .
For a composite tuple , the aggregate alignment requirement is determined by the maximum alignment of its constituent components:
The total footprint in bytes equals the sum of member sizes plus padding bytes inserted to satisfy individual offsets :
For a fixed array of size with elements of type :
4. Slice Types & Fat Pointers#
Arrays have fixed lengths known at compile time. Slices, denoted as [T], represent dynamically sized views into contiguous sequences. Because the length is not fixed at compile time, a slice reference &[T] is stored as a fat pointer.
On a 64-bit architecture, a fat pointer requires 16 bytes (two 64-bit words):
- Pointer Word (8 bytes): The 64-bit starting address of the backing memory buffer.
- Length Word (8 bytes): The number of elements contained in the slice view.
1234567891011121314151617fn print_subsequence(slice: &[i32]) { println!("Length: {}", slice.len()); println!("Base pointer: {:p}", slice.as_ptr()); for item in slice { print!("{} ", item); } println!(); } fn main() { let full_array = [100, 200, 300, 400, 500]; // Borrow a view of elements at index 1 to 3 let view: &[i32] = &full_array[1..4]; print_subsequence(view); }
5. Concept Verification#
Verify your grasp of Rust scalar memory representation and fat pointers:
Why does a slice reference &[T] require 16 bytes of memory on a 64-bit platform, whereas a normal reference &T requires 8 bytes?
How does Rust handle integer overflow when running binaries compiled in release mode?