Loops
Basics · Lesson 11
Rust has three loops: for over a range or collection, while, and loop (forever, until break).
Coming from Gofor i in 1..4 counts 1, 2, 3 like for i := 1; i < 4; i++, while n > 0 is for n > 0, and loop is for {}. There is no three-part for and no n++, but break can hand back a value from a loop.
Coming from KotlinWatch the ranges: Rust's 1..4 stops before 4 like Kotlin's 1 until 4, and Rust's 1..=4 is Kotlin's 1..4. There is no n++ (write n += 1), and loop is while (true) where break can give back a value.
Coming from Pythonfor i in 1..4 is for i in range(1, 4) and loop is while True. The one new thing is that break tries * 10 can hand a value back out of a loop.
Coming from TypeScriptRust has no C-style for (let i = 1; i < 4; i++) and no ++. You loop over a range instead (for i in 1..4), and loop is while (true) where break can hand back a value.
Coming from Javafor i in 1..4 is for (int i = 1; i < 4; i++); Rust has no C-style three-part for and no ++, so you write n += 1. loop is while (true), except break can hand back a value.
Coming from C#for i in 1..4 is like foreach (var i in Enumerable.Range(1, 3)), except that 1..4 gives start and end (4 excluded) where Range gives start and count. Rust has no three-part for or ++. loop is while (true) where break value returns a result, so the result can be an ordinary let instead of a variable set inside the loop.
Coming from CRust has no three-part for (init; test; step): you loop over a range like 1..4 instead, and n++ is gone, so write n += 1. loop is for (;;), and break can carry a value out of it.
Coming from C++for i in 1..4 is a range-based for over a half-open range, like for (int i : std::views::iota(1, 4)) in C++20, and there is no classic three-part for or ++. loop is while (true) with a break that returns a value, so you don't need the lambda trick.
The quick comparison
fn main() { // 1, 2, 3 (the end is not included) for i in 1..4 { println!("for {i}"); }
let mut n = 3; while n > 0 { n -= 1; }
// loop runs until break. break can hand back a value. let mut tries = 0; let found = loop { tries += 1; if tries == 3 { break tries * 10; } }; println!("n={n} found={found}");}for 1 for 2 for 3 n=0 found=30
package main
import "fmt"
func main() { for i := 1; i < 4; i++ { fmt.Println("for", i) }
n := 3 for n > 0 { n-- }
tries, found := 0, 0 for { tries++ if tries == 3 { found = tries * 10 break } } fmt.Printf("n=%d found=%d\n", n, found)}for 1 for 2 for 3 n=0 found=30
fun main() { for (i in 1 until 4) { println("for $i") }
var n = 3 while (n > 0) { n-- }
var tries = 0 var found = 0 while (true) { tries++ if (tries == 3) { found = tries * 10 break } } println("n=$n found=$found")}for 1 for 2 for 3 n=0 found=30
# 1, 2, 3 (the end is not included)for i in range(1, 4): print(f"for {i}")
n = 3while n > 0: n -= 1
# no loop-with-value in Python: use while True and set a variabletries = 0while True: tries += 1 if tries == 3: found = tries * 10 breakprint(f"n={n} found={found}")for 1 for 2 for 3 n=0 found=30
// 1, 2, 3 (the end is not included)for (let i = 1; i < 4; i++) { console.log(`for ${i}`);}
let n = 3;while (n > 0) { n -= 1;}
// break can't hand back a value, so set a variable before breakinglet tries = 0;let found: number;while (true) { tries += 1; if (tries === 3) { found = tries * 10; break; }}console.log(`n=${n} found=${found}`);for 1 for 2 for 3 n=0 found=30
public class Main { public static void main(String[] args) { // 1, 2, 3 (the end is not included) for (int i = 1; i < 4; i++) { System.out.println("for " + i); }
int n = 3; while (n > 0) { n--; }
// break can't hand back a value, so set a variable first int tries = 0; int found; while (true) { tries++; if (tries == 3) { found = tries * 10; break; } } System.out.println("n=" + n + " found=" + found); }}for 1 for 2 for 3 n=0 found=30
// 1, 2, 3for (var i = 1; i < 4; i++){ Console.WriteLine($"for {i}");}
var n = 3;while (n > 0){ n--;}
// C# break can't hand back a value, so keep it in a variablevar tries = 0;int found;while (true){ tries++; if (tries == 3) { found = tries * 10; break; }}Console.WriteLine($"n={n} found={found}");for 1 for 2 for 3 n=0 found=30
#include <stdio.h>
int main(void) { // 1, 2, 3 (the end is not included) for (int i = 1; i < 4; i++) { printf("for %d\n", i); }
int n = 3; while (n > 0) { n -= 1; }
// C's break cannot hand back a value, so set a variable first int tries = 0; int found; for (;;) { tries += 1; if (tries == 3) { found = tries * 10; break; } } printf("n=%d found=%d\n", n, found); return 0;}for 1 for 2 for 3 n=0 found=30
#include <iostream>
int main() { // 1, 2, 3 (the end is not included) for (int i = 1; i < 4; ++i) { std::cout << "for " << i << "\n"; }
int n = 3; while (n > 0) { n -= 1; }
// break can't hand back a value, so a lambda that returns does the job int tries = 0; const int found = [&] { while (true) { tries += 1; if (tries == 3) { return tries * 10; } } }(); std::cout << "n=" << n << " found=" << found << "\n";}for 1 for 2 for 3 n=0 found=30
What's different
1..4is 1, 2, 3: the end is left out.1..=4includes the 4.- There is no C-style
for i := 0; i < n; i++. Usefor i in 0..n. (0..10).rev()counts down.(0..10).step_by(2)skips. A range that starts above its end, like4..0, is empty: it does not count down.
Questions people ask
Can a for loop give back a value with break value, like loop does?
No. Only loop can hand back a value with break value. A for or while loop has the value (), and the compiler stops you if you write break value in one.
The reason: a for loop can finish without ever reaching break, for example on an empty list. Then there would be no value to assign. A loop can only end through break, so a value always comes out.
In practice you rarely need either. When you want a value from a loop, use an iterator method. Each one is an expression and says what you mean:
find: the first item that matches. It returns an Option, because there might be none.position: the index of the first match, also an Option.sum,count,max,any,all: one value from the list.map(...).collect(): a new list.
fn main() { let nums = vec![3, 8, 12, 5]; let found = for n in &nums { if *n > 10 { break *n; } };}error[E0571]: `break` with value from a `for` loop --> src/main.rs:4:22 | 3 | let found = for n in &nums { | -------------- you can't `break` with a value in a `for` loop 4 | if *n > 10 { break *n; } | ^^^^^^^^ can only break with a value inside `loop` or breakable block | help: use `break` on its own without a value inside this `for` loop | 4 - if *n > 10 { break *n; } 4 + if *n > 10 { break; }
fn main() { let nums = vec![3, 8, 12, 5];
// only `loop` can hand back a value with break let mut i = 0; let idx = loop { if nums[i] > 10 { break i; } i += 1; };
// what people usually write: iterator methods are expressions let first_big = nums.iter().find(|&&n| n > 10); let position = nums.iter().position(|&n| n > 10); let total: i32 = nums.iter().sum(); let squares: Vec<i32> = nums.iter().map(|n| n * n).collect(); println!("{idx} {first_big:?} {position:?} {total} {squares:?}");}2 Some(12) Some(2) 28 [9, 64, 144, 25]
Using n++
Looks fine: in Go, Kotlin, TypeScript, Java, C#, C and C++ it runs fine; in Python it runs but gives a wrong answer. Rust won't compile it.
Works in Go. In Rust it's just a rule.Works in Kotlin. In Rust it's just a rule.Broken in Python. Rust catches it.Works in TypeScript. In Rust it's just a rule.Works in Java. In Rust it's just a rule.Works in C#. In Rust it's just a rule.Works in C. In Rust it's just a rule.Works in C++. In Rust it's just a rule.fn main() { let mut n = 0; n++; println!("{n}");}package main
import "fmt"
func main() { n := 0 n++ fmt.Println(n)}1
fun main() { var n = 0 n++ println(n)}1
n = 0++n # n++ is a syntax error; ++n is two unary plusesprint(n)0
let n = 0;n++;console.log(n);1
public class Main { public static void main(String[] args) { int n = 0; n++; System.out.println(n); }}1
var n = 0;n++;Console.WriteLine(n);1
#include <stdio.h>
int main(void) { int n = 0; n++; printf("%d\n", n); return 0;}1
#include <iostream>
int main() { int n = 0; n++; std::cout << n << "\n";}1
What does the compiler say? Guess first, then open.
error: Rust has no postfix increment operator --> src/main.rs:3:6 | 3 | n++; | ^^ not a valid postfix operator | help: use `+= 1` instead | 3 - n++; 3 + n += 1;
Go runs this and prints 1. n++ is fine in Go as a statement on its own.
Kotlin runs this and prints 1. In Rust you write the same thing as n += 1.
n++ is a syntax error in Python, but ++n runs and prints 0. It is read as two unary pluses, so n never changes.
Prints 1. n++ is normal TypeScript; Rust has no ++ and wants n += 1.
Java has ++, so this prints 1. Rust has no ++; write n += 1 instead.
n++ is everyday C# and prints 1. Rust has no ++ and asks for n += 1.
C has n++, so this prints 1. In Rust you write n += 1.
C++ has ++, so this prints 1. In Rust you write n += 1.
Rust has no ++ or --. Write n += 1.
Rust has no ++ or --; += 1 is the one way to add one. ++ inside other expressions (like a[i++] = i) is a classic source of confusion about what happens first. Go keeps n++ but only as a statement; Rust drops it.
The fix
Compiles and runs.
The toolkit
Rust has three loops: for goes through a sequence of values, while repeats while a bool condition holds, and loop repeats until a break. while let is a while that tests a pattern. There is no three-part for and no do ... while (what replaces them). Outputs come from Linux on 64-bit ARM with Rust 1.99.1.
| Loop | Use it when | Its value |
|---|---|---|
for x in seq { } |
you have a range, an array or another sequence | () |
while cond { } |
a bool decides whether to go again |
() |
while let Some(x) = expr { } |
you keep taking items until none is left | () |
loop { } |
there is no natural condition; you leave with break |
whatever follows break |
Only loop can break with a value (let x = loop { break 5; };). Statements and expressions shows the error for and while give if you try.
Ranges
A range is a value that stands for a run of numbers (or chars): a..b stops before b and a..=b includes it. The end is read once, when the loop starts.
fn main() { // a..b stops before b; a..=b includes b for i in 1..4 { println!("1..4: {i}"); } for i in 1..=4 { println!("1..=4: {i}"); }
// ranges of char work too for c in 'a'..='c' { println!("'a'..='c': {c}"); }
// counting down: reverse a range for i in (1..=3).rev() { println!("rev: {i}"); }
// every third number for i in (0..10).step_by(3) { println!("step_by(3): {i}"); }
// a range that starts above its end is empty: no error, no countdown let mut runs = 0; for _ in 4..0 { runs += 1; } println!("runs of 4..0: {runs}");
// the end is read once, when the loop starts let mut n = 3; let mut passes = 0; for _ in 0..n { n = 5; passes += 1; } println!("n is now {n}, passes: {passes}");}1..4: 1 1..4: 2 1..4: 3 1..=4: 1 1..=4: 2 1..=4: 3 1..=4: 4 'a'..='c': a 'a'..='c': b 'a'..='c': c rev: 3 rev: 2 rev: 1 step_by(3): 0 step_by(3): 3 step_by(3): 6 step_by(3): 9 runs of 4..0: 0 n is now 5, passes: 3
The trap is 4..0: it is not a countdown. It is empty, so the body never runs, with no error. rev and step_by are methods on the range (Iterators); wrap the range in parentheses before calling one. step_by counts from the front of whatever it receives, so order matters.
fn main() { // the order matters: step_by counts from the front of whatever it is given print!("rev then step_by(3):"); for i in (1..10).rev().step_by(3) { print!(" {i}"); } println!();
print!("step_by(3) then rev:"); for i in (1..10).step_by(3).rev() { print!(" {i}"); } println!();}rev then step_by(3): 9 6 3 step_by(3) then rev: 7 4 1
Looping over a list
A for loop takes anything that produces items one at a time, such as an array. .iter() hands out references to the items; .enumerate() pairs each with its position, and (i, name) unpacks the pair:
fn main() { let scores = [10, 20, 30];
// an array can be looped over directly for s in scores { println!("score: {s}"); }
// enumerate pairs each item with its position let names = ["ann", "bo"]; for (i, name) in names.iter().enumerate() { println!("name {i}: {name}"); }}score: 10 score: 20 score: 30 name 0: ann name 1: bo
Looping Over a Collection covers for x in v, &v and &mut v.
The loop variable
The name after for is a new variable on every pass, and it belongs to the loop. It cannot be assigned unless you write mut, and even then only that pass sees the change. Python’s for and TypeScript’s for...of work the same way; the three-part for of C, Java, C#, Go and TypeScript keeps one counter that the body can change.
fn main() { let mut passes = 0; // `mut i` is allowed, but it is a copy for this pass only for mut i in 0..5 { if i == 1 { i += 2; } passes += 1; println!("i: {i}"); } println!("passes: {passes}");}i: 0 i: 3 i: 2 i: 3 i: 4 passes: 5
Five passes, whatever the body does to i. Without mut the assignment does not compile, and after the loop the name is gone:
fn main() { for i in 0..3 { i += 1; println!("{i}"); }}error[E0384]: cannot assign twice to immutable variable `i` --> src/main.rs:3:9 | 2 | for i in 0..3 { | - first assignment to `i` 3 | i += 1; | ^^^^^^ cannot assign twice to immutable variable | help: consider making this binding mutable | 2 | for mut i in 0..3 { | +++
fn main() { for i in 0..3 { println!("inside: {i}"); } println!("after: {i}");}error[E0425]: cannot find value `i` in this scope --> src/main.rs:5:23 | 5 | println!("after: {i}"); | ^ not found in this scope
loop, break and continue
break leaves the loop. continue skips the rest of this pass. In a loop, break value makes the whole loop evaluate to that value, so the result can be a plain let instead of a mut variable. A loop with the test at the end runs the body first, as do ... while does elsewhere:
fn main() { // loop is the only loop that gives back a value: break carries it out let mut guess = 1; let first_over_100 = loop { guess *= 3; if guess > 100 { break guess; } }; println!("first power of 3 over 100: {first_over_100}");
// a do-while: run the body once, then decide let mut n = 0; loop { n += 5; if n >= 3 { break; } } println!("n after the body ran once: {n}");}first power of 3 over 100: 243 n after the body ran once: 5
A step you write yourself
A step that is not +1 (i *= 2, two cursors walking towards each other) is a while with the step in the body. The catch: continue skips the rest of the pass, including a step or test written at the end. The last two loops below would run on, so each has a guard:
fn main() { // a step that is not +1 is a `while` with the step written by hand print!("doubling:"); let mut i = 1; while i < 100 { print!(" {i}"); i *= 2; } println!();
// `continue` skips the rest of the pass, including a step written at the end let mut j = 0; let mut passes = 0; while j < 3 && passes < 6 { // (the `passes` guard only stops this demo) passes += 1; if j == 1 { continue; } j += 1; } println!("while: j = {j}, passes = {passes}");
// the same goes for the test at the end of a `loop` that stands in for `do ... while` let mut k = 0; let mut passes = 0; loop { k += 1; passes += 1; if passes == 6 { break; // guard, only for this demo } if k >= 3 { continue; // in C, `continue` in a do-while goes on to the test } if !(k < 3) { break; } } println!("loop: k = {k}, passes = {passes}");}doubling: 1 2 4 8 16 32 64 while: j = 1, passes = 6 loop: k = 6, passes = 6
Labels
A name written 'outer: in front of a loop lets break 'outer and continue 'outer act on that loop from inside an inner one. (The ' is the one lifetimes use; Rust at a glance lists both.) break 'label value works on a labelled loop, and a plain block can carry a label too and be left with a value.
fn main() { // continue 'outer skips to the next n as soon as one divisor is found 'outer: for n in 2..20 { for d in 2..n { if n % d == 0 { continue 'outer; } } println!("prime: {n}"); }
// break 'label value leaves a labelled loop and gives back a value let pair = 'search: loop { for a in 1..10 { for b in a..10 { if a * b == 24 && a + b == 10 { break 'search (a, b); } } } break (0, 0); }; println!("pair: {pair:?}");}prime: 2 prime: 3 prime: 5 prime: 7 prime: 11 prime: 13 prime: 17 prime: 19 pair: (4, 6)
A break inside a match leaves the enclosing loop, not the match (why there is no fall-through):
fn main() { // `match` is not a loop and not a switch: `break` here leaves the `for` for n in 1..=5 { match n { 3 => break, _ => println!("n: {n}"), } } println!("after the loop");}n: 1 n: 2 after the loop
while let
while let PATTERN = expr runs the body while the pattern fits, and stops the first time it does not. The usual partner is Vec::pop, which returns Some(item) while the list has items and None when it is empty (Option, Vectors). It suits work that the loop itself adds to. This one walks 1 to 7 as a tree (n has children 2n and 2n + 1), taking the last child first:
fn main() { // a worklist: each pass may add more work, which `for x in &work` would not allow let mut work = vec![1]; let mut order = Vec::new(); while let Some(n) = work.pop() { order.push(n); if n * 2 <= 7 { work.push(n * 2); work.push(n * 2 + 1); } } println!("visited: {order:?}"); println!("work left: {work:?}");}visited: [1, 3, 7, 6, 2, 5, 4] work left: []
A for over the same list does not compile: the loop is still using the list when push changes it (Borrowing explains the rule):
fn main() { let mut work = vec![1, 2]; for n in &work { work.push(n * 2); }}error[E0502]: cannot borrow `work` as mutable because it is also borrowed as immutable --> src/main.rs:4:9 | 3 | for n in &work { | - ----- | | | | | immutable borrow occurs here | _____| immutable borrow later used here | | 4 | | work.push(n * 2); | | ^^^^^^^^^^^^^^^^ mutable borrow occurs here 5 | | } | |_____- this for loop borrows `work` immutably, preventing mutation within its body | = help: consider using an index-based loop instead, or collecting modifications into a separate collection
Counting down with usize
Lengths and indexes are usize, an unsigned integer (Declarations and types). Rust will not index with an i32, and an unsigned counter goes below zero:
fn main() { let items = [5, 6, 7]; let i: i32 = 2; println!("items[i] = {}", items[i]);}error[E0277]: the type `[{integer}]` cannot be indexed by `i32` --> src/main.rs:4:37 | 4 | println!("items[i] = {}", items[i]); | ^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32` ... (notes pointing into the standard library trimmed)
#![allow(unused_comparisons)] // silences the compiler's "this is always true" warning
fn main() { let items = [5, 6, 7]; let mut i = items.len() - 1; // a usize is never below 0, so `i >= 0` is always true while i >= 0 { println!("items[{i}] = {}", items[i]); i -= 1; }}items[2] = 7 items[1] = 6 items[0] = 5 thread 'main' (1) panicked at src/main.rs:9:9: attempt to subtract with overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The panic comes from i -= 1 at 0: “attempt to subtract with overflow”. The compiler also warns about the test i >= 0 by default; here that lint is turned into an error to show the message in full:
#![deny(unused_comparisons)] // this lint is a warning by default; deny makes it an error
fn main() { let items = [5, 6, 7]; let mut i = items.len() - 1; while i >= 0 { println!("items[{i}] = {}", items[i]); i -= 1; }}error: comparison is useless due to type limits --> src/main.rs:6:11 | 6 | while i >= 0 { | ^^^^^^ | note: the lint level is defined here --> src/main.rs:1:9 | 1 | #![deny(unused_comparisons)] // this lint is a warning by default; deny makes it an error | ^^^^^^^^^^^^^^^^^^
That panic is a debug-build check (a plain cargo run; Hello, World covers the build kinds). A --release build turns overflow-checks off (the Rust Book, Cargo profiles), and i -= 1 then wraps to the largest usize. This program would still panic, at items[i], as the next one does; a loop that does not index would have nothing to stop it.
fn main() { let items = [5, 6, 7]; // what `i -= 1` gives at 0 when overflow checks are off: wrapping_sub always wraps let i = std::hint::black_box(0usize).wrapping_sub(1); println!("i: {i}"); println!("items[i] = {}", items[i]);}i: 18446744073709551615 thread 'main' (1) panicked at src/main.rs:6:31: index out of bounds: the len is 3 but the index is 18446744073709551615 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Count down with a reversed range instead. It never subtracts, and an empty list is just an empty range:
fn show(items: &[i32]) { // (0..len).rev() counts len-1, ..., 0 and never subtracts below zero for i in (0..items.len()).rev() { println!("items[{i}] = {}", items[i]); } println!("done, {} items", items.len());}
fn main() { show(&[5, 6, 7]); show(&[]);}items[2] = 7 items[1] = 6 items[0] = 5 done, 3 items done, 0 items
Your language, in Rust
Going deeper
What for does
The Reference says for calls IntoIterator::into_iter on the expression and then calls next() in a loop, leaving at None. That is why anything that produces items works, and why a range is an ordinary value rather than special loop syntax (IntoIterator is a trait, a set of abilities; Traits). The match below takes Some(i) while an item comes and None at the end:
fn main() { // what `for i in 0..3 { ... }` does: ask for the next item until there is none let mut it = 0..3; loop { match it.next() { Some(i) => println!("manual: {i}"), None => break, } }
// a range is an ordinary value with fields and methods let r = 2..5; println!("start: {}, end: {}", r.start, r.end); println!("len: {}", r.len()); println!("contains 4: {}", r.contains(&4)); println!("contains 5: {}", r.contains(&5));}manual: 0 manual: 1 manual: 2 start: 2, end: 5 len: 3 contains 4: true contains 5: false
..= reaches the last value
0..=255u8 includes 255, the largest u8, and the loop still ends. 0..255u8 stops one short:
fn main() { let mut inclusive = 0; for _ in 0..=255u8 { inclusive += 1; } let mut exclusive = 0; for _ in 0..255u8 { exclusive += 1; } println!("0..=255u8 runs: {inclusive}"); println!("0..255u8 runs: {exclusive}");}0..=255u8 runs: 256 0..255u8 runs: 255
No float ranges
0.0..1.0 is a valid range of f64, but Range implements Iterator only for select types, particularly integers. Count with an integer, then scale:
fn main() { for x in 0.0..1.0 { println!("{x}"); }}error[E0277]: `std::ops::Range<{float}>` is not an iterator --> src/main.rs:2:14 | 2 | for x in 0.0..1.0 { | ^^^^^^^^ `Range<{float}>` is not an iterator | = help: the nightly-only, unstable trait `Step` is not implemented for `{float}` = note: `Range` only implements `Iterator` for select types in the standard library, particularly integers; to see the full list of types, see the documentation for the unstable `Step` trait = help: the following other types implement trait `Step`: i128 i16 i32 i64 i8 isize u128 u16 and 4 others = note: required for `std::ops::Range<{float}>` to implement `Iterator` = note: required for `std::ops::Range<{float}>` to implement `IntoIterator`
fn main() { // count with an integer, then scale for i in 0..5 { let x = i as f64 * 0.25; println!("x: {x}"); }}x: 0 x: 0.25 x: 0.5 x: 0.75 x: 1
Practice
Try it
Open each exercise in the Playground, replace the todo!(), and run it until it prints all checks passed.
Collatz steps, then a first square
collatz_steps counts the steps to reach 1 (even: halve, odd: triple and add one) with a while loop; 6 takes 8 steps and 1 takes none. first_square_over finds the smallest k whose square exceeds a limit with a loop that ends in break k.
/// How many steps it takes to reach 1: an even n becomes n / 2, an odd n becomes 3 * n + 1./// `n` is at least 1. 6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 is 8 steps.fn collatz_steps(n: u64) -> u32 { todo!("a `while` loop that runs until n is 1 and counts the steps")}
/// The smallest k whose square is greater than `limit`.fn first_square_over(limit: u32) -> u32 { todo!("a `loop` that tries k = 1, 2, ... and ends with `break k` (no `mut` result needed)")}
fn main() { assert_eq!(collatz_steps(1), 0); assert_eq!(collatz_steps(2), 1); assert_eq!(collatz_steps(6), 8); assert_eq!(collatz_steps(27), 111);
assert_eq!(first_square_over(0), 1); assert_eq!(first_square_over(1), 2); assert_eq!(first_square_over(24), 5); assert_eq!(first_square_over(25), 6); assert_eq!(first_square_over(100), 11);
println!("all checks passed");}Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.
Show a solution
/// How many steps it takes to reach 1: an even n becomes n / 2, an odd n becomes 3 * n + 1./// `n` is at least 1. 6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 is 8 steps.fn collatz_steps(mut n: u64) -> u32 { let mut steps = 0; while n != 1 { if n % 2 == 0 { n /= 2; } else { n = 3 * n + 1; } steps += 1; } steps}
/// The smallest k whose square is greater than `limit`.fn first_square_over(limit: u32) -> u32 { let mut k = 1; loop { if k * k > limit { break k; } k += 1; }}
fn main() { assert_eq!(collatz_steps(1), 0); assert_eq!(collatz_steps(2), 1); assert_eq!(collatz_steps(6), 8); assert_eq!(collatz_steps(27), 111);
assert_eq!(first_square_over(0), 1); assert_eq!(first_square_over(1), 2); assert_eq!(first_square_over(24), 5); assert_eq!(first_square_over(25), 6); assert_eq!(first_square_over(100), 11);
println!("all checks passed");}all checks passed
Count the primes on a stack
Count the primes in a Vec by popping numbers off its end (the function owns the list; Ownership explains). stack.pop() gives Some(n) while there are numbers and None when the list is empty (Option), so while let Some(n) = stack.pop() is the loop. Try the divisors 2..n; when one divides n, a labelled continue goes to the next popped number. 0 and 1 are not prime.
/// How many of the numbers in `stack` are prime (2, 3, 5, 7, 11, ...). 0 and 1 are not.fn count_primes(mut stack: Vec<u32>) -> u32 { todo!("`while let Some(n) = stack.pop()`; a labelled `continue` when a divisor is found")}
fn main() { assert_eq!(count_primes(vec![2, 3, 4, 5, 6, 7]), 4); assert_eq!(count_primes(vec![97, 100]), 1); assert_eq!(count_primes(vec![9, 15, 21]), 0); // 0 and 1 are not prime, and an empty stack is fine assert_eq!(count_primes(vec![0, 1]), 0); assert_eq!(count_primes(vec![]), 0);
println!("all checks passed");}Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.
Show a solution
/// How many of the numbers in `stack` are prime (2, 3, 5, 7, 11, ...). 0 and 1 are not.fn count_primes(mut stack: Vec<u32>) -> u32 { let mut primes = 0; 'next: while let Some(n) = stack.pop() { if n < 2 { continue; } for d in 2..n { if n % d == 0 { continue 'next; } } primes += 1; } primes}
fn main() { assert_eq!(count_primes(vec![2, 3, 4, 5, 6, 7]), 4); assert_eq!(count_primes(vec![97, 100]), 1); assert_eq!(count_primes(vec![9, 15, 21]), 0); // 0 and 1 are not prime, and an empty stack is fine assert_eq!(count_primes(vec![0, 1]), 0); assert_eq!(count_primes(vec![]), 0);
println!("all checks passed");}all checks passed
Read more
- The Rust Book, chapter 3.5: Control Flow, and 3.2: Data Types (the Integer Overflow section).
- The Rust Reference: Loop expressions, with the exact expansion of
for. - Rust by Example: for and range, nesting and labels and while let.
- The standard library:
RangeandRangeInclusive.
Learn Rust is an independent project, not affiliated with the Rust Project or the Rust Foundation. Code and text are under the MIT licence.Source on GitHub.