Problem overview
The function max_in_array receives a vector of integers, nums, and returns the largest value in it. The task asks you to write the loop yourself instead of calling a built-in maximum function, because the loop is the point of the exercise.
For nums = [2, 7, 11, 8, 11, 8, 3, 11] the answer is 11. The value appears three times, but you only return the value, not a position or a count.
Approach: keep the best value seen so far
You can't know the maximum until you have looked at every element, so the function visits each one once. It keeps a single variable that holds the largest value seen so far. Whenever an element is bigger than that variable, the variable takes the new value. When the loop ends, the variable holds the maximum of the whole vector.
The one decision to make is the starting value. Starting at 0 looks natural, but it is wrong for a vector like [-5, -2, -9]: no element is greater than 0, so the function would return 0, a number that is not in the vector. Start from the first element instead. That value really is in the vector, and every other element is compared against it.
Step by step
- Declare
max_valwithlet mutand set it tonums[0]. It needsmutbecause the loop will reassign it, and Rust variables are immutable unless you say otherwise. - Loop over
&numswith a variablen. Writing&numsborrows the vector, so the loop reads the elements without taking ownership of them. - Compare inside the loop. Each
nis a reference, an&i32, whilemax_valis a plaini32. Writing*nreads the number the reference points to, and that is what you compare withmax_val. - Update
max_valto*nwhen the comparison is true. - Return
max_valafter the loop.
The solution in Rust
impl Solution {
fn max_in_array(nums: Vec<i32>) -> i32 {
let mut max_val = nums[0];
for n in &nums {
if *n > max_val {
max_val = *n;
}
}
return max_val;
}
}
The Rust details worth a closer look:
- References in a
forloop. Looping over&numsyields&i32items. Writingif n > max_valdoes not compile: the compiler reports mismatched types, expecting a reference and finding an integer. Dereferencing with*nfixes it. - Copying out of a reference.
max_val = *n;copies the integer, which is allowed becausei32is aCopytype. The vector is untouched. - Why borrow at all? A loop over
numswithout the&moves the vector into the loop, and it can't be used afterwards. Here the function doesn't use it afterwards, so either form works, but borrowing is the habit to build. returnat the end. The explicitreturn max_val;is valid. Idiomatic Rust often leaves the final value as an expression without a semicolon,max_val, which means the same thing.
Complexity
Time: O(n). The loop visits each of the n elements once, and each visit does a constant amount of work.
Space: O(1). The function stores one extra integer, max_val, no matter how large the vector is.
Edge cases and common mistakes
- All negative numbers.
[-5, -2, -9]returns-2. A start value of 0 would return 0. - Starting from the smallest possible value.
i32::MINalso works as a start, butnums[0]needs no extra constant and keeps the answer an actual element. - A single element.
[42]returns42: the loop compares the element with itself and changes nothing. - An empty vector.
nums[0]panics with "index out of bounds: the len is 0 but the index is 0". The lesson's tests use non-empty vectors; a function meant for general use would return anOption<i32>instead. - Using
>=instead of>. The result is the same, because assigning an equal value changes nothing. It only does extra assignments. - Forgetting
mut. Without it,max_val = *n;is a compile error, since the variable can't be assigned a second time.
Testing
[2, 7, 11, 8, 11, 8, 3, 11]gives11.[-5, -2, -9]gives-2.[42]gives42.[3, 3, 3]gives3.
