Part 5. Iterating manually (10 points)
Similar to the previous part, you will implement a function and some unit tests.
Your task
Write a function that iterates over the elements in its argument and adds them all and returns the sum.
#![allow(unused)] fn main() { fn manual_sum(data: &[i32]) -> i32 { let mut data_iter = data.iter(); todo!("Implement me") } }
The catch is that for this task, you may not use a for
loop. Instead, you’ll
need to work with the iterator returned by data.iter()
directly. Iterators
in Rust work similarly to iterators in Java. To implement this as a Java
method using iterators rather than a for
loop we might use something like
this.
int manualSum(ArrayList<Integer> data) {
int sum = 0;
Iterator<Integer> iter = data.iterator();
while (iter.hasNext()) {
sum += iter.next();
}
return sum;
}
Iterators in Rust don’t have separate has_next()
and next()
methods.
Instead, there’s just a single next()
method that returns an
Option<T>
. Every Option
is either None
or Some(blah)
for some
value blah
. Here’s a simple example of using an Option
.
#![allow(unused)] fn main() { let x: Option<i32> = Some(10); assert!(x.is_some()); assert!(!x.is_none()); assert_eq!(x.unwrap(), 10); let y: Option<i32> = None; assert!(y.is_none()); assert!(!y.is_some()); }
We can use is_some()
or is_none()
to determine if an Option
is a
Some
or a None
. If we have a Some
, then we can unwrap it by
calling unwrap()
which returns the data. If we try calling unwrap()
on
None
, we’ll get a runtime error.
Write some unit tests. Make sure you cover all of the following cases.
- Empty
data
(where the sum should be 0); - Single-element vectors; and
- Vectors with multiple elements.
Implement manual_sum
by calling next()
on data_iter
repeatedly until the
result is None
. Unwrap all of the other values and sum them up.