We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 14 of 24
if is an expression
FizzBuzz, as data. For 1..=n, produce "Fizz" for multiples of three,
"Buzz" for multiples of five, "FizzBuzz" for multiples of both, and the
number written out otherwise.
pub fn fizzbuzz_codes(n: u32) -> Vec<String>
n = 15 gives the familiar fifteen strings. n = 0 gives []. n = 1 gives
["1"].
The constraint: every element must come from one if/else if expression
bound to one value. No early returns, no pushing from inside the branches.
The starter does not compile, and its error message is the one that makes
“if is an expression” click.
No ternary, because none is needed
Rust has no ?: operator, and it does not need one, because if already
produces a value:
let label = if n > 0 { "positive" } else { "non-positive" };
This is strictly better than a ternary. It reads the same in the two-branch case, it scales to five branches without becoming unreadable, and it is the same syntax you already use for control flow — one construct, not two.
Two rules make it work:
-
Every branch must have the same type. The whole
ifhas one type, so all its arms must agree. -
If there is no
else, the type must be(). Anifwithout anelsecan produce nothing when the condition is false, and the only value that can stand in for “nothing” is unit. Solet x = if c { 5 };is an error, andif c { do_thing(); }as a statement is fine.
The error you are here to meet
E0308 has several faces, and this problem shows two of them.
Face one: “expected bool, found integer”. Rust’s if takes a bool and
only a bool. There is no truthiness — if 1 and if some_vec are both type
errors. That one costs about four seconds and then never again.
Face two: the dedicated “if and else have incompatible types”. This is
what the starter produces:
error[E0308]: `if` and `else` have incompatible types
|
| "Fizz"
| ------ expected because of this
...
| i.to_string()
| ^^^^^^^^^^^^^ expected `&str`, found `String`
Three branches produce &str — borrowed text living in the binary — and one
produces String — owned text on the heap. They are genuinely different types
and the if has to be one of them. The fix is to make every branch produce a
String, which is the type the Vec needs anyway.
Note what rustc did for you: it named the branch that set the expectation
and the branch that disagreed. Reading both halves of that message is the
whole skill from item 0.2.
is_multiple_of instead of % == 0
You would probably write i % 15 == 0, and clippy 1.95 will stop you:
manual_is_multiple_of fires on that for unsigned integers and suggests
i.is_multiple_of(15).
This is a genuinely new method — stabilised in Rust 1.87 — and the lint has a
real argument behind it: is_multiple_of says the intent in words, and it does
not silently panic when the divisor turns out to be zero, whereas % 0 does.
It only exists for unsigned types, which is why you will still write % 2 == 0
for an i32 and see no lint.
Two more default-on lints in the neighbourhood
-
if_same_then_else— two branches with identical bodies, which is either a copy-paste bug or a chain that wants collapsing. -
needless_bool—if c { true } else { false }isc.
Both are the kind of thing you write at 2am, and both are free.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.