03. Basic Types (Integer, Float, bool, char, tuple, array)
Rust's scalar types (i8/u8/i32/i64/f32/f64/bool/char) and compound types (tuple/array) — all in one lesson. The differences from other languages: integer overflow handling, char being a 4-byte Unicode scalar, and arrays having compile-time fixed size. (Heap-allocated dynamic vectors come in the next lesson cluster.)
What you'll learn
- 1Tell apart i32 / u32 / i64 / usize and pick the right one
- 2Know whether f32 or f64 is the default
- 3Understand that `char` is 4 bytes, not 1, and represents a full Unicode scalar
- 4Construct tuples and arrays, deconstruct via patterns and indexing
- 5Know how overflow differs between debug and release builds
Overview
Rust's basic types are explicit and honest. `i32` guarantees a 32-bit signed integer, no platform-dependent surprises like C's `int`. One exception: `usize` is **a pointer-sized unsigned integer** used for sizes and indices.
Core Concepts
1) Integer types — width × signedness matrix
| Width | Signed | Unsigned |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 (default) | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| pointer | isize | usize |
- **i32** is the default inferred type for integer literals
- **usize** is used everywhere for indices — `vec[5]` takes a usize
- **Overflow** — debug builds panic, release builds wrap (both are safe, no UB)
2) Float types
- **f64** is the default — same speed as f32 on modern CPUs but with twice the precision
- **f32** is for graphics / SIMD / embedded where memory matters
3) bool and char
`bool` is true / false. `char` uses single quotes and is a **4-byte Unicode scalar value** — `'a'`, `'한'`, `'😀'` are all single chars.
4) Compound types
- **tuple** `(T1, T2, ...)` — packs different types together
- **array** `[T; N]` — N elements of the same type, fixed-size, on the stack
Hands-on Examples
Explicit type annotations:
fn main() {
let a: i32 = -10;
let b: u8 = 255; // u8 max
let pi: f64 = 3.141592;
let yes: bool = true;
let k: char = '한';
let smile: char = '😀';
println!("a={}, b={}, pi={}, yes={}, k={}, smile={}", a, b, pi, yes, k, smile);
}Tuple destructuring and array indexing:
fn main() {
let point = (3.0, 4.0); // (f64, f64)
let (x, y) = point; // pattern destructuring
println!("x={}, y={}", x, y);
let arr: [i32; 5] = [10, 20, 30, 40, 50];
println!("first={}, last={}", arr[0], arr[4]);
// arr[5] panics at runtime (safe — no UB)
}Overflow — debug and release behave differently:
fn main() {
let x: u8 = 255;
let y = x + 1; // debug: panic
// release: 0 (wrapping)
println!("{}", y);
}Common Mistakes
Q. Isn't `char` one byte, like in C?
A. Rust's `char` is **4 bytes**, a Unicode scalar value. C's `char` is different. UTF-8 bytes are `u8` or `[u8]`.
Q. usize vs. u32 for indices?
A. **usize**. Collection lengths and indices are uniformly `usize`. To go between u32 and usize you need an explicit `as usize`.
Q. How do I make a runtime-sized array?
A. You can't with `[T; N]`. Use **`Vec<T>`** — heap-allocated, dynamic. Covered in a later lesson.
Recap
- Default integer = i32, indices = usize, default float = f64
- char is 4 bytes (Unicode)
- tuple destructures via patterns, array is fixed `[T; N]`
- Overflow = debug panic / release wrap — both safe, no UB
Try It Yourself
- Print `i32::MAX` and add 1 to observe debug-build panic
- Build two points as tuples and compute the Euclidean distance
- Make a tuple `("Jane", 30, 180.5)` and destructure-print
All lecture materials and example code are openly available on GitHub.
View on GitHub ↗