Skip to content
Lesson 3 of 15

Step 1 of 5 · Reading · ~3 min

Learn

Getting Started

Numbers and Math

By the end of this lesson you can do integer arithmetic in Rust without the three classic surprises: silent truncation, a mixed-type compile error, and overflow.

Types never mix silently

Every value has one numeric type, and arithmetic needs both sides to match. Rust never converts for you:

let apples: i32 = 3;
let weight: f64 = 1.5;
// apples + weight          -> compile error: cannot add `f64` to `i32`
let total = apples as f64 + weight;   // 4.5

as is the explicit cast. Widening (i32 to i64 or f64) is safe. Narrowing is lossy on purpose: 300_i32 as u8 is 44, because only the low 8 bits survive. The _i32 suffix pins a literal's type, and underscores are digit separators: 1_000_000.

Integer division truncates; % is the remainder

When both operands are integers, / throws away the fraction (toward zero) and % gives what was thrown away:

expressionresult
17 / 53
17 % 52
4 / 90
4 % 94
17.0 / 5.03.4

The two always fit back together: (a / b) * b + a % b == a. That makes them the tool for splitting a quantity into whole units plus a rest. A worked trace, turning 135 minutes into hours and minutes:

let minutes: i64 = 135;
let hours = minutes / 60;     // 135 / 60 = 2
let rest = minutes % 60;      // 135 % 60 = 15
println!("{}h {}m", hours, rest);   // 2h 15m

Order matters as soon as a division sits inside a longer formula, because every / truncates at the moment it runs. Take 7 boxes of 3 eggs split between 4 cooks:

let boxes: i64 = 7;
let per_box: i64 = 3;
let cooks: i64 = 4;
println!("{}", boxes * per_box / cooks);  // 21 / 4 = 5   (correct)
println!("{}", boxes / cooks * per_box);  // 1 * 3  = 3   (fraction lost early)

Multiply first, divide last.

Overflow is a bug, and Rust treats it like one

An i32 holds up to 2,147,483,647. A debug build panics with attempt to multiply with overflow the moment a result goes past that; a release build wraps around silently. So pick a type that is wide enough: i64 reaches about 9.2 * 10^18. Two numbers near 10^9 multiplied give a product near 10^18, which needs i64. When wrapping is really what you want, say so with a method: wrapping_add, checked_add (gives None on overflow), saturating_add (stops at the maximum).

Floats have their own rules

f64 is IEEE-754: 0.1 + 0.2 is not exactly 0.3, so never compare floats with ==. Math lives in methods: 2.0_f64.sqrt() is 1.4142135623730951, 3_i32.pow(4) is 81. Converting to float just to divide changes the answer's shape: 17 as f64 / 5 as f64 prints 3.4, not 3.

Common mistakes

  • Dividing before multiplying. a / c * b truncates early; with 7, 3 and 4 you get 3 instead of 5.
  • Swapping / and %, so the whole-unit count and the rest come out in the wrong order.
  • Converting to f64 to divide. You print 0.99 or 3.4 where the task wants the whole-number quotient 0 or 3.
  • Doing the arithmetic in i32. Anything past 2,147,483,647 panics or wraps; keep it in i64.

Your exercise

A rectangular plot of land is shared between people in whole square metres. The starter reads the three numbers as i64 and prints placeholder zeros; replace them with the area, each person's whole share, and what is left over. The usual slip is dividing one side by the number of people before multiplying.

Up nextStrings: String and &strStrings

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…