Skip to content
Numbers and Math
step 1/5

Reading — step 1 of 5

Learn

~3 min readGetting Started

Numbers and Math

Rust's arithmetic is strict in exactly the places other languages are sloppy — and every strictness maps to a bug you now won't have. Three rules cover the territory.

Rule 1: types never mix silently

let a: i32 = 3;
let b: f64 = 1.5;
a + b                  // ERROR: cannot add `f64` to `i32`
a as f64 + b           // 4.5 — conversion written where reviewers can see it

Even i32 + i64 refuses to compile. as is the quick cast — and it's explicitly lossy when narrowing (300_i32 as u8 is 44; it truncates bits, no questions asked), which is why it's reserved for cases you've thought about. That noisiness is the point: in Rust, every place precision could be lost has an as you can grep for.

Rule 2: integer and float division are different operations

7 / 2        // 3     — integer division truncates toward zero
7 % 2        // 1     — remainder
7.0 / 2.0    // 3.5   — float division

Same as Go, same trap: 99 / 100 == 0, and every percentage calculation on integers is wrong until you convert: a as f64 / b as f64. The % operator earns its keep in the classic places — divisibility (n % 3 == 0), wraparound (i % len), digit peeling (n % 10, n / 10).

Rule 3: overflow is a bug, and Rust treats it like one

An i32 maxes out at 2,147,483,647. Add one more and:

  • Debug builds panic: attempt to add with overflow — loud, immediate, at the exact line.
  • Release builds wrap (for speed), like C — but you're expected to have caught it in debug.

When overflow behavior is intentional, Rust makes you say which kind: a.wrapping_add(b) (wrap around), a.checked_add(b) (returns an OptionNone on overflow), a.saturating_add(b) (clamps at the max). Hash functions want wrapping; account balances want checked. The method name documents the decision — this is very Rust.

Floats: the universal caveats, plus methods

f64 is IEEE-754, so the classics apply: 0.1 + 0.2 != 0.3, never == floats (compare (a - b).abs() < 1e-9), never floats for money. What's different is ergonomics — math lives in methods on the numbers themselves:

let x: f64 = 2.0;
x.sqrt()               // 1.4142135623730951
x.powi(10)             // 1024.0  (integer exponent)
x.powf(0.5)            // sqrt, via float exponent
(-3.5_f64).abs()       // 3.5
9_i32.pow(2)           // 81 — integers have pow too

(That 2.0_f64 / 9_i32 suffix syntax pins a literal's type when inference needs a hint — and underscores work as digit separators anywhere: 1_000_000.)

Your exercise: Rectangle Area

Read width and height, multiply, print. The engineering content is entirely in the edges: decide i64 vs f64 from the problem's examples (are inputs 12 or 2.5? is expected output 24 or 24.00?), reuse the stdin-parsing pattern from last lesson, and format output to match exactly — {} prints 24.0 for a float where a grader may want 24.00 ({:.2} — next lesson makes formatting systematic). One multiplication, three decisions; that ratio is what real programming feels like.

Discussion

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

Sign in to post a comment or reply.

Loading…