Checkpoint: Basics
Seven short programs, one question each, taken from the eight Basics lessons (Hello, World through Tuples) and the Syntax lessons before them. Decide first: does it compile, and what does it print? If it does not compile, which error code?
RememberPredict first, then look. When your guess and the run disagree, the lesson linked under the answer says why.
Each program is a file in a Cargo project (src/bin/q1.rs and so on) and runs with cargo run --bin q1. The answers are real runs in the page’s image (Rust 1.99.1, edition 2024, a debug build).
1. What does it print?
Section titled “1. What does it print?”fn main() { let a = -7 / 2; let b = -7 % 3; let c = (-7_i32).rem_euclid(3); let d = 7 / 2 * 2; let e = 0.1 + 0.2 == 0.3; println!("{a} {b} {c} {d} {e}");}Answer
$ cargo run --bin q1
-3 -1 2 6 false
Integer / cuts toward zero, so -7 / 2 is -3, and % takes the sign of the left side. rem_euclid gives a result of zero or more. 7 / 2 * 2 goes left to right: 3, then 6. And 0.1 + 0.2 is not exactly 0.3 as a float, so the comparison is false. See Division and remainder and Floats.
2. What does it print?
Section titled “2. What does it print?”fn main() { let mut found = 0; 'outer: for i in 1..=4 { for j in i..=4 { if (i * j) % 3 == 0 { found += 1; continue 'outer; } if i + j > 6 { break 'outer; } println!("{i},{j}"); } } println!("found {found}");}Answer
$ cargo run --bin q2
1,1
1,2
2,2
found 3
continue 'outer skips the rest of the inner loop and starts the next i: that is what happens at (1,3), (2,3) and (3,3), each a multiple of 3, so found reaches 3 and none of them is printed. i..=4 includes 4. At (4,4) the product is 16, but i + j is 8, so break 'outer ends both loops. See Labels and Ranges.
3. Does it compile? What does it print?
Section titled “3. Does it compile? What does it print?”fn main() { let target = 5; for n in [3, 5, 8] { match n { target => println!("{n} is the target"), } }}Answer
$ cargo run --bin q3
warning: unused variable: `target`
--> src/bin/q3.rs:2:9
|
2 | let target = 5;
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_target`
|
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
warning: unused variable: `target`
--> src/bin/q3.rs:5:13
|
5 | target => println!("{n} is the target"),
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_target`
warning: `checkpoint-basics` (bin "q3") generated 2 warnings (run `cargo fix --bin "q3" -p checkpoint-basics` to apply 2 suggestions)
3 is the target
5 is the target
8 is the target
It compiles, with two warnings, and every number takes the arm. A name in a pattern is a new variable, not a comparison with the target above it, so target => matches anything and binds it. The warnings are the clue: both target variables are unused. To compare, use a const, or a guard such as n if n == target. See A name in a pattern binds, it does not compare.
4. Which error code?
Section titled “4. Which error code?”fn main() { let n = 3; let label; if n > 0 { label = "positive"; } println!("{label}");}Answer
error[E0381]: used binding `label` is possibly-uninitialized --> src/bin/q4.rs:7:16 | 3 | let label; | ----- binding declared here but left uninitialized 4 | if n > 0 { | ----- if this `if` condition is `false`, `label` is not initialized 5 | label = "positive"; 6 | } | - an `else` arm might be missing here, initializing `label` 7 | println!("{label}"); | ^^^^^ `label` used here but it is possibly-uninitialized | = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown
let label; is allowed, but the compiler follows every path, and the one where n > 0 is false never sets label. The note on the if is the hint: add an else, or give label a value when you declare it. See Declared now, set later.
5. Which error code?
Section titled “5. Which error code?”fn size(n: u8) -> &'static str { match n { 0..=9 => "small", 10..=99 => "medium", }}
fn main() { println!("{}", size(42));}Answer
error[E0004]: non-exhaustive patterns: `100_u8..=u8::MAX` not covered --> src/bin/q5.rs:2:11 | 2 | match n { | ^ pattern `100_u8..=u8::MAX` not covered | = note: the matched value is of type `u8` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 4 ~ 10..=99 => "medium", 5 ~ 100_u8..=u8::MAX => todo!(),
A u8 holds 0 to 255, and the arms stop at 99. The message names the values left over, so you do not have to work them out. See Every case must be covered.
6. Which error code?
Section titled “6. Which error code?”fn main() { let a = [10, 20, 30]; let i: i32 = 1; println!("{}", a[i]);}Answer
error[E0277]: the type `[{integer}]` cannot be indexed by `i32` --> src/bin/q6.rs:4:22 | 4 | println!("{}", a[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)
It is E0277, not the E0308 that a mismatch of two number types gives. Indexing is a trait question: an array indexes like a slice, by usize or by ranges of usize, and i32 is neither. Declare i as a usize, or convert it. See Indexes are usize.
7. Fix it
Section titled “7. Fix it”This compiles. What happens when you run it, and what is one change that fixes it?
fn sum_below(n: usize) -> usize { (0..=n - 1).sum()}
fn main() { println!("{}", sum_below(4)); println!("{}", sum_below(0));}Answer
6
thread 'main' (140) panicked at src/bin/q7.rs:2:10:
attempt to subtract with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceThe first call printed 6. In the second, n - 1 is 0 - 1 on a usize, and a debug build (what cargo run makes) stops with a panic. In a release build the lesson says the check is off and the subtraction wraps; this page’s runner builds only debug, so that is not run here. One fix, the one this course uses, is to write 0..n, which never subtracts, and an empty range adds up to 0. (Others exist, such as n.saturating_sub(1) or checking n == 0 first.) The number in brackets after thread 'main' is a thread id and can differ on your machine:
fn sum_below(n: usize) -> usize { (0..n).sum()}
fn main() { println!("{}", sum_below(4)); println!("{}", sum_below(0));}6 0
See Overflow and Counting down with usize.
How to score yourself
Section titled “How to score yourself”Count a question as right when you gave the correct output, or the correct error code and the reason. Questions 1 and 2 are about reading code the way the compiler does: left to right, one operator at a time, one loop at a time. If you got them wrong, redo them on paper, writing the value of each variable after every line.
Questions 3 to 6 are about what the compiler refuses or warns about, and the answers show that warnings are part of the output, and a warning counts as part of “does it compile?”: the program in question 3 runs, but the compiler had already told you something was wrong. Question 7 is the one program that compiles and then stops, here in a debug build.
If you missed one
Section titled “If you missed one”- Question 1: Numbers and types
- Questions 2 and 7: Loops
- Questions 3 and 5: Match
- Question 4: Variables
- Question 6: Numbers and types
Next is Checkpoint: Ownership.
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.