Level 1 · Sprout

Data Types 🔢

Numbers, text, yes/no switches: every value in Rust has a type, and the compiler always knows which one. Meet the raw ingredients.

Why types matter

A type tells the computer what kind of thing a value is and how much memory it needs. Adding two numbers makes sense; adding a number to the word "banana" doesn't. Rust checks all of this before your program runs, so type mix-ups can never crash it later.

Usually you don't even write types; Rust infers them from context. But you can always be explicit with a colon:

let lucky = 7;        // Rust infers: i32
let lucky: i8 = 7;    // you insist: a tiny i8

Whole numbers: the integer family

Integers come in sizes (how many bits of memory) and two flavors: i for signed (can be negative), u for unsigned (never negative).

TypeRangeUse it for…
i32±2.1 billionthe default: almost everything
i64±9.2 quintillionvery big counts, timestamps
u80 to 255bytes, color channels
u320 to 4.3 billioncounts that can't be negative
usizemachine-sizedindexes and lengths of collections
let population: u32 = 8_000_000;  // underscores = readable
let depth: i32 = -11_034;         // negative needs a signed type

Decimal numbers: floats

let pi = 3.14159;      // f64: the default, double precision
let ratio: f32 = 0.5;  // f32: half the memory, less precise
⚠️ Oil and water Rust never mixes types silently. let sum = 5 + 0.5; is an error: is that integer math or decimal math? You decide, with a cast: 5 as f64 + 0.5. Explicit beats surprising.

true / false: bool

let is_hungry = true;
let has_snacks = false;
let panic_time = is_hungry && !has_snacks; // && = AND, ! = NOT, || = OR
println!("Panic? {panic_time}");

Booleans fuel every decision your program makes; they're the answer to every comparison: 5 > 3 is true, x == y asks "equal?", x != y asks "different?".

Single characters: char

let grade = 'A';      // single quotes for chars
let crab = '🦀';      // yes, emoji are chars; Rust speaks Unicode
let heart = '❤';

Text: &str (for now)

let greeting = "Hello, sailor!"; // double quotes for text

Text in double quotes is a string literal, type &str. Rust actually has two string types (the other is String), and the difference is one of the most interesting stories in the language. It needs ownership (Lesson 6) to make sense, so we'll park it until Lesson 10. For now, double quotes just work.

Bundles: tuples and arrays

Tuples group a few values of any mix of types, like one row on a scoreboard:

let player = ("Rusty", 42, true);  // name, score, is_online
let (name, score, online) = player;      // unpack all at once
println!("{name} scored {score} (online: {online})");
println!("score again: {}", player.1);   // or grab by position: .0, .1, .2

Arrays hold a fixed number of values of the same type, like an egg carton:

let tides = [1.2, 0.8, 1.5, 0.9];
let first = tides[0];              // positions start at ZERO
let week = [0; 7];                 // shortcut: seven zeros
println!("first tide: {first}, days: {}", week.len());
🛡️ Safety, even here Ask for tides[99] and Rust stops the program with a clear message instead of silently reading random memory like C would. That silent reading (a buffer overflow) is one of the most exploited security bugs in history. In Rust, it's simply not a thing.
Exercise 1

Type detective

Without running it, name the type of each variable:

let a = 42;
let b = 42.0;
let c = '4';
let d = "42";
let e = 4 > 2;
Reveal answers

a: i32 (integer default) · b: f64 (float default) · c: char (single quotes) · d: &str (double quotes) · e: bool (a comparison's answer is always true/false).

Exercise 2

Trading card

Make a tuple for a creature: name (&str), power level (i32), and legendary status (bool). Unpack it and print a sentence like Krabby has power 9000 (legendary: true).

Reveal solution
fn main() {
    let card = ("Krabby", 9000, true);
    let (name, power, legendary) = card;
    println!("{name} has power {power} (legendary: {legendary})");
}
Exercise 3

Meal plan

Create an array of three meal names. Print the first and last elements, using .len() to find the last index (careful: positions start at zero!).

Reveal solution
fn main() {
    let meals = ["pancakes", "ramen", "tacos"];
    println!("breakfast: {}", meals[0]);
    println!("dinner: {}", meals[meals.len() - 1]);
}

Forgetting the - 1 asks for index 3 in a 3-item array (positions 0, 1, 2). Try it and read the panic message. Now you've seen Rust's bounds checking with your own eyes.