What the code is supposed to do
The function check_for_positive receives a vector of integers and returns true if at least one of them is greater than zero, and false if none is. The program that calls it prints Array has positive numbers for [-1, 2, 3]. This is the code you start with:
fn check_for_positive(nums: Vec<i32>) -> bool {
for number in nums {
if number > 0 {
return true;
} else {
return false;
}
}
}
// Do not change this code:
fn main_function() {
if check_for_positive(vec![-1, 2, 3]) {
println!("Array has positive numbers");
} else {
println!("Array doesn't have positive numbers");
}
}
The Rust compiler rejects it with error[E0308]: mismatched types. It says that a bool was expected and () was found, and it adds that for loops evaluate to the unit type ().
What goes wrong
The logic bug: giving up after the first element
Both branches of the if contain a return. So the loop never gets to look at a second element: whatever the first number is, the function answers immediately. For [-1, 2, 3] the first number is -1, which is not positive, so the else branch returns false without ever seeing the 2 and the 3.
One number that is not positive says nothing about the others. The question is whether any element is positive, and the answer false is only justified once every element has been checked.
The compile error: no value after the loop
The function promises a bool. The body ends with the for loop, and a loop produces no value, only (). The compiler can't prove that the loop always returns, because it can't be sure the vector is non-empty. For an empty vector the loop would finish at once, and the function would have nothing to return. That is the mismatch the error describes.
Both problems have the same cure: the else branch goes away, and a final false is added after the loop.
Fixing it step by step
- Remove the
elsebranch. A non-positive number should simply let the loop continue with the next element. - Keep
return trueinside theif. One positive number is enough to answer, so the function can stop immediately. - Add
return falseafter the loop. It runs only if the loop finished without finding a positive number, including for an empty vector.
The fixed code in Rust
fn check_for_positive(nums: Vec<i32>) -> bool {
for number in nums {
if number > 0 {
return true;
}
}
return false;
}
fn main_function() {
if check_for_positive(vec![-1, 2, 3]) {
println!("Array has positive numbers");
} else {
println!("Array doesn't have positive numbers");
}
}
For [-1, 2, 3]: -1 is not positive, so the loop moves on. 2 is positive, so the function returns true and the program prints Array has positive numbers. For [-1, -2] no element qualifies, the loop ends, and the function returns false.
Tracing both versions
Following the original code on [-1, 2, 3] shows the logic bug directly. The loop starts with number = -1. The test -1 > 0 is false, so the else branch runs and returns false. The function is finished after one pass, and 2 and 3 are never read.
In the fixed version, the same first pass finds the test false and does nothing, because there is no else. The second pass has number = 2, the test is true, and the function returns true. The third element is never read, but it doesn't need to be.
The worst case for the fixed version is a vector with no positive number. Every element is checked, the loop ends, and the line after it returns false.
The same idea with an iterator
Rust has a ready-made form of this search. nums.iter().any(|&x| x > 0) returns true as soon as one element passes the test, and false when none does, with the same result on every vector, including an empty one. For vec![-1, 2, 3] it gives true, for vec![-1, -2] it gives false, and for an empty vector it gives false. The lesson uses the explicit loop, which shows what any does for you.
Rust details
- Everything is an expression. A function's body evaluates to its last expression. A
forloop is an expression of type(), so a function that ends with one can't have a return type other than(). - Adding only the final
falseis not enough. It would make the code compile, but the earlyreturn falsewould remain, and[-1, 2, 3]would still givefalse. Both problems have to be fixed. return false;versusfalse. Writingfalsewithout a semicolon on the last line is the idiomatic form, and it means the same as the lesson'sreturn false;.- The vector is consumed.
for number in numsmoves the vector into the loop, and eachnumberis ani32that needs no dereference.
Common mistakes
- Returning
falseinside the loop. It reports the answer for the first element only. - Treating "not positive" as a final answer. Only the positive case ends the search early. The negative case means "keep looking", and that asymmetry is what the fixed loop expresses.
- Assuming the compiler error is the whole bug. Silencing the error with a closing
falsemakes the program build but leaves the wrong answer for[-1, 2, 3]. After a fix compiles, run the example to check the result. - Using
>= 0. Zero is not positive, so[0]must givefalse. - Printing instead of returning. The function returns a
bool, andmain_functiondoes the printing.
Testing
[-1, 2, 3]givestrue.[-1, -2, -3]givesfalse.[0]givesfalse.[]givesfalse.
