Problem overview
The function has_passed receives a vector of integer grades and returns true when their average is 5 or more. The average is the sum of the grades divided by how many there are.
For grades = [4, 7, 5, 9, 8, 2] the sum is 35 and there are 6 grades, so the average is 35 / 6, about 5.83. That is at least 5, so the result is true. For [4, 7, 5, 3, 8, 2] the sum is 29 and the average is about 4.83, so the result is false.
Approach
The problem splits into three small jobs:
- add up all the grades;
- divide the sum by the number of grades;
- compare the average with 5.
The first job is the same accumulator pattern as any sum. A variable starts at zero, a loop visits each grade, and each pass adds the grade to the variable. The other two jobs take one line each. One pass over the vector with a few variables gives O(n) time and O(1) extra space.
Step by step
- Declare the sum as an
f32.let mut sum = 0.0f32;is a mutable floating-point variable. Making it a float means the later division keeps its fractional part, so the average is 5.83 and not a rounded 5. - Loop over a reference to the vector.
for grade in &gradesborrows the vector instead of moving it. That matters because the length is needed again after the loop. - Add each grade, converted. Looping over
&gradesgives a&i32for each element. The*reads the number behind the reference, andas f32converts it. Rust never mixes integer and float types in arithmetic on its own, so the cast is required. - Divide by the length.
grades.len()is ausize, so it is also converted withas f32before the division. - Compare with 5.0. The average is an
f32, so the threshold is a float literal. Writing5would not compile against a float.
The solution in Rust
impl Solution {
fn has_passed(grades: Vec<i32>) -> bool {
let mut sum = 0.0f32;
for grade in &grades {
sum += *grade as f32;
}
let average = sum / grades.len() as f32;
return average >= 5.0;
}
}
Some Rust details here:
- The
0.0f32literal. The suffix sets the type of the literal. Without it, the compiler would work out the type from later use, and here it would still arrive atf32. - Precedence of
as. In*grade as f32, the dereference happens first and then the cast. Ingrades.len() as f32, the method call comes first. - No implicit conversions. Adding an
i32to anf32, or dividing anf32by ausize, is a compile error. Each value has to be converted by hand. - The comparison is the return value.
average >= 5.0already produces abool, so noifis needed. f32precision. It holds about seven significant digits. For the sums in this lesson, that is plenty. The comparison to 5.0 is exact when the true average is exactly 5, such as for[5, 5, 5].
Complexity
Time: O(n). The loop makes one pass over the n grades, with constant work on each. Reading the length is O(1).
Space: O(1). The function stores sum, average and the loop variable, no matter how many grades there are.
Edge cases and common mistakes
- Exactly 5. An average of 5 passes, because the comparison is
>=.[5]and[5, 5, 5]both givetrue. - An empty vector. The sum is 0.0 and the length is 0, so the division is
0.0 / 0.0, which is NaN (not a number). Any comparison with NaN is false, so the function returnsfalseand does not panic. An integer division by zero, by contrast, would panic. - Forgetting a cast.
sum += grade;with a floatsumand an&i32does not compile. The same goes for dividing bygrades.len()without the cast. - Cutting the average with integer division. In Rust,
35 / 6is5, because integers divide without a fraction. For this particular check, the answer still comes out right, since an average below 5 always truncates to 4 or less. The float version is still the better habit, because it keeps the real average if you ever need to print it. - Dividing by the wrong count. The divisor is the number of grades, not the largest grade or the last index.
Testing
[4, 7, 5, 9, 8, 2]givestrue.[4, 7, 5, 3, 8, 2]givesfalse.[5]givestrue.[5, 5, 5]givestrue.[]givesfalse.
