Primitive Types in Rust

Essential
Last updated: Tags: Rust

Prerequisites

Every value in Rust has a type, and the simplest types are built into the language. These primitive types are the atoms from which all other types are constructed.

Integer types

Rust has signed and unsigned integer types at five widths plus a pointer-width pair:

SignedUnsignedWidth
i8u88 bits
i16u1616 bits
i32u3232 bits
i64u6464 bits
i128u128128 bits
isizeusizepointer-width (64 bits on 64-bit systems)

The signed types use two’s complement. A u8 holds [0,255][0, 255]; an i8 holds [128,127][-128, 127].

usize is the type of array indices and memory lengths — the natural integer size for addressing memory. If you’re coming from Zig, the mapping is direct: Zig’s u8, i32, usize, and isize correspond exactly.

let a: u8 = 255;
let b: i32 = -1_000_000; // underscores as separators are allowed
let c: usize = 42;

Integer overflow: in debug builds Rust panics on integer overflow; in release builds it wraps. For explicit wrapping semantics use .wrapping_add(), .wrapping_mul(), and similar methods.

Floating-point types

Rust provides two IEEE 754 floating-point types:

  • f32 — 32-bit single precision
  • f64 — 64-bit double precision (the default for float literals)
let x: f32 = 1.5;
let y = 2.71828;   // inferred as f64

Arithmetic between f32 and f64 is not implicit — you must cast with as.

The boolean type

bool holds exactly two values: true and false. It is one byte in memory.

let flag = true;
let combined = flag && false; // false

Rust does not treat integers as booleans. if 1 { } is a compile error; you must provide a bool expression.

The character type

char is a Unicode scalar value — a single Unicode code point, stored as four bytes.

let letter = 'a';
let crab = '🦀';

char literals use single quotes; string literals use double quotes. A Rust char is not a byte: iterating a string with .chars() yields code points, while .bytes() yields raw bytes.

The unit type

() is the unit type — “void” in other languages. It has exactly one value, also written (). A function with no stated return type returns () implicitly.

fn greet(name: &str) { // return type is ()
    println!("Hello, {name}!");
}

Unit appears naturally in generics: Result<(), String> is a result that succeeds with no useful value and fails with a String.

Numeric literals

let decimal     = 98_222;      // underscores for readability
let hex         = 0xff;
let octal       = 0o77;
let binary      = 0b1111_0000;
let byte: u8    = b'A';        // byte literal — u8 only

Casting with as

The as keyword performs explicit numeric conversions:

let wide: i32 = 300;
let narrow = wide as u8; // truncates: 300 % 256 = 44

Conversions between numeric types are always explicit in Rust. There is no implicit widening or narrowing. Be aware of truncation and sign changes when casting to a narrower type.