Reading — step 1 of 5
Learn
Variables and Mutability
Here is Rust's first genuinely contrarian decision: variables don't vary unless you ask.
let x = 5;
x = 6; // ERROR: cannot assign twice to immutable variable `x`
let mut y = 5; // `mut` = "I intend to change this"
y = 6; // fine
Every other mainstream language defaults to mutable and offers const as an opt-in. Rust flips it: immutable by default, mut as a declaration of intent. The payoff compounds: when you read someone's code (including last-month-you's), every variable that can change is marked at its birthplace. Everything else is a fixed fact. Whole categories of "who changed this?!" debugging simply don't exist.
The compiler enforces it both ways — it errors if you mutate a non-mut, and warns if you mark something mut and never change it. The annotations stay honest.
Types: inferred, but real
Rust is statically typed with strong inference:
let count = 42; // i32 — the default integer
let price = 9.99; // f64 — the default float
let active = true; // bool
let letter = 'A'; // char (single quotes; a full Unicode code point)
let size: u64 = 1024; // annotate when you want a specific type
The integer menu is explicit about width and signedness: i8/i16/i32/i64/i128 signed, u8–u128 unsigned, usize for indexing/lengths. Defaults (i32, f64) are right until a protocol or index says otherwise. And as in Go — no implicit conversions, ever: i32 + f64 is a compile error until you write as f64 (or better, from/into). Where numbers change type, the code says so.
Integer overflow gets Rust's signature treatment: in debug builds it panics (crashes with a message) instead of silently wrapping. The bug you'd chase for a day elsewhere announces itself on first run.
Shadowing: the idiom that surprises
Rust lets you let the same name again — and it's not reassignment, it's a new variable that shadows the old one:
let input = "42"; // &str
let input: i32 = input.parse().unwrap(); // now an i32 — same name, new variable
This is idiomatic and useful: transformation pipelines keep one honest name (input) instead of input_str, input_parsed, input_final. Note what shadowing can do that mut can't — change the type — and that it works without mut because nothing is mutated; the old variable simply stops being visible.
Constants
const MAX_RETRIES: u32 = 3;
const requires the type annotation, a compile-time value, and SCREAMING_SNAKE_CASE by convention. Use it for genuine program-wide constants.
Your exercise: Sum Two Numbers
Read two integers from stdin, print their sum. Reading input in Rust is more ceremony than you're used to — here's the pattern, worth keeping on a mental index card:
use std::io::Read;
fn main() {
let mut input = String::new();
std::io::stdin().read_to_string(&mut input).unwrap();
let mut it = input.split_whitespace();
let a: i64 = it.next().unwrap().parse().unwrap();
let b: i64 = it.next().unwrap().parse().unwrap();
println!("{}", a + b);
}
read_to_string slurps all of stdin, and split_whitespace doesn't care whether the numbers were separated by spaces or newlines — this exact pattern works for every input layout the graders use, so it's worth memorizing as-is. Notice who needs mut (the input buffer and the iterator — both get modified) and who doesn't (a, b — read, never changed). The unwrap()s say "crash if this fails" — a placeholder you'll replace with real error handling in chapter 6, and perfectly acceptable for grader input that's promised to be well-formed.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…