We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 16 of 24
Loop labels and labeled blocks
Find a value in a grid of rows and return its [row, column], or [-1, -1] if
it is not there. Scan row by row, left to right, and stop at the first match.
pub fn find_in_grid(grid: &[Vec<i32>], target: i32) -> Vec<i64>
Rows may have different lengths — that is legal and there is a test for it. An
empty grid gives [-1, -1].
The starter does not compile: it uses a label nobody declared.
break leaves one loop. A label says which one.
Plain break leaves the innermost enclosing loop. In a nested search that is
almost never what you want — you have found the answer, and you would like to
leave both loops.
The C-shaped answer is a flag threaded through both levels:
let mut found = false;
for row in grid {
for value in row {
if *value == target { found = true; break; }
}
if found { break; }
}
That is four extra lines of bookkeeping whose only job is to carry one bit upward, and it is exactly the kind of hand-maintained state Rust otherwise helps you avoid. So the language lets you name a loop:
'outer: for row in grid {
for value in row {
if *value == target {
break 'outer; // leaves BOTH loops
}
}
}
A label is a lifetime-style name — an apostrophe followed by an identifier —
written before the loop keyword. break 'name and continue 'name then target
that loop specifically. Using a label that does not exist is E0426: use of undeclared label, which is what the starter produces. (Its cousin E0767,
“use of unreachable label”, is what you get when the label exists but is not on
an enclosing construct.)
Labeled blocks: the part most people never learn
Since Rust 1.65 you can label a plain block, and break out of it with a value:
let answer = 'search: {
for (r, row) in grid.iter().enumerate() {
for (c, value) in row.iter().enumerate() {
if *value == target {
break 'search vec![r as i64, c as i64];
}
}
}
vec![-1, -1]
};
Read it as a small, structured early return that only escapes as far as the
block. The break 'search expr supplies the block’s value; if the loops finish
without breaking, the block falls through to its own tail expression, which is
the not-found answer. Both paths produce the same type, so the whole block is an
expression you can bind — or, as here, make the entire function body.
This is worth knowing well. It gives you the readability of an early return
without actually returning, which matters as soon as there is cleanup to do
after the search, and it keeps the found and not-found answers visibly adjacent
instead of separated by the whole loop.
never_loop
One default-on lint to know about here: never_loop fires when a loop body
can only ever execute once — typically because every path through it ends in
break or return. That usually means you wrote a loop where you wanted an
if, and it is a genuine bug catcher.
Types at the boundary
enumerate() gives usize indices; the return type is Vec<i64> so that the
sentinel -1 is expressible. as i64 from usize is a widening on the 64-bit
graders and cannot lose anything here.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.