Rust arrays are bounds-checked — but only at runtime, when the index isn't a literal the compiler can check in advance. An off-by-one like asking for index len() instead of len() - 1 compiles cleanly: there's nothing wrong with the type, only the value. The program builds, runs, and panics the moment it actually reaches that line, with no warning beforehand.
fn main() {
let scores: [i32; 3] = [90, 85, 78];
let last_index = scores.len();
println!("{}", scores[last_index]);
}
// index out of bounds: the len is 3 but the index is 3Rust’s scalar types are integers (signed i8–i128, unsigned u8–u128, plus pointer-sized isize/usize), floating-point (f32, f64), bool, and char (a 4-byte Unicode scalar value, unlike a single byte in C). Compound types group multiple values into one: a tuple bundles a fixed number of values of possibly different types, and an array holds a fixed number of values of the SAME type, with its length baked into the type itself.
Integers: the width is in the name
Unlike C's platform-dependent int, a Rust integer type states its exact bit width and signedness directly: i32 is a signed 32-bit integer, u8 an unsigned 8-bit integer (a "byte"). i32 is the default when a type isn't specified and can't be inferred otherwise.
let a: i32 = -42;
let b: u8 = 255; // max value for an 8-bit unsigned int
let c = 100; // inferred as i32, the default
// let bad: u8 = 256; // COMPILE ERROR: 256 doesn't fit in a u8 (0-255)Tuples: fixed-size, mixed types
A tuple groups a fixed number of values, each of any type, accessed by position with dot notation or unpacked via destructuring.
let person: (&str, i32, bool) = ("Alice", 30, true);
println!("{}", person.0); // "Alice" — access by position
let (name, age, _active) = person; // destructuring
println!("{} is {}", name, age);Arrays: fixed-size, same type
Unlike a tuple, every element of an array is the same type, and the length is part of the type ([i32; 5] and [i32; 10] are different types) — for a growable list of the same type, Rust code reaches for Vec<T> instead, covered in the next chapter.
let scores: [i32; 3] = [90, 85, 78];
println!("{}", scores[0]); // 90
let zeros = [0; 5]; // shorthand: [0, 0, 0, 0, 0]That's the trap in the bug above: scores.len() is 3 for a 3-element array, but valid indices only run 0 to 2 — asking for index 3 panics with "index out of bounds." Rather than doing the off-by-one arithmetic by hand, methods like .last() sidestep indexing entirely and return an Option instead of panicking:
fn main() {
let scores: [i32; 3] = [90, 85, 78];
let last = scores.last().expect("scores is never empty");
println!("{}", last);
}
// 78