We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 1 of 24
let, immutability by default, and mut
For every position in a slice, report the largest value seen up to and including that position.
pub fn running_max(values: &[i32]) -> Vec<i32>
[3, 1, 4, 1, 5] becomes [3, 3, 4, 4, 5]. An empty slice gives an empty
vector. [-1, -9] gives [-1, -1] — the running maximum never goes back down.
The starter does not compile, and the error it produces is the most important default in the language.
let binds; it does not declare a variable
let best = i32::MIN;
best = 7; // error[E0384]: cannot assign twice to immutable variable
In C, Java, Python, Go and JavaScript, a name is mutable unless you go out of
your way. In Rust it is the other way round. let x = … binds a value to a
name once. To rebind it later you must ask, at the point of declaration:
let mut best = i32::MIN;
best = 7; // fine
This is not a nanny rule about style. It is load-bearing, and here is why: the borrow checker’s whole job is to know, at every point in the program, who is allowed to change what. If everything were mutable by default, “who can change this?” would have no small answer anywhere, and the guarantees Rust sells — no data races, no use-after-free, without a garbage collector — would not be derivable. Immutability by default is the floor that ownership is built on. You are meeting it in Track 1 so that in Track 3 it is already familiar.
There is a practical payoff too. When you read a function and see let without
mut, you know that name’s value never changes for the rest of the block. That
is one less thing to hold in your head, on every line, forever.
And the reverse: unused_mut
The gate runs both ways. A let mut you never actually mutate is a warning —
unused_mut — and warnings are errors here. So you cannot buy safety by
sprinkling mut everywhere “just in case”. Each mut has to be earned by an
actual assignment.
This problem needs exactly two mutable bindings: the running best, and the vector you are building. A third, added defensively, will fail the gate.
The shape of the loop
for &v in values {
// v is an i32 here, copied out of the slice
}
values is a &[i32] — a borrowed view of a run of integers. Iterating it
yields &i32, references to each element. The &v in the pattern position
undoes that reference and gives you the i32 itself. That works because
i32 is a Copy type: copying one is just moving four bytes, so Rust does it
implicitly and nothing is lost.
Track 1 uses only Copy types for exactly this reason. The mental model
“Rust behaves like C ints” is correct here, it is comfortable, and Track 2 will
deliberately break it the moment you meet String and Vec. Enjoy it while it
lasts.
Starting value
i32::MIN is the smallest i32, so any real element beats it. That is the
standard trick for a running maximum, and it is why the empty-input case works
without a special branch: the loop simply never runs.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.