Refactoring with iterators

By this point, your code probably contains a number of places where you iterate over the elements of a list to, e.g., print its content, count its elements, fill an empty vector with them, etc. As fans of the DRY principle we cannot stand this situation, let's refactor our way out of this! Obviously, the situation calls for the introduction of an iterator over list elements.

Exercise 3.a: Add to your List data type a iter() method that returns a type implementing the Iterator trait. When done, the following should work out of the box:

#![allow(unused)]
fn main() {
let l = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
let mut i = l.iter();
assert_eq!(i.next(), Some(1));
assert_eq!(i.next(), Some(2));
assert_eq!(i.next(), Some(3));
assert_eq!(i.next(), None);
}

Exercise 3.b: Make your List implement IntoIterator, so that your lists can work out of the box with for loops, like this:

#![allow(unused)]
fn main() {
let l = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
for e in &l {
    println!("list element: {e}");
}
}

Exercise 3.c: Refactor previous code you have written for List (and/or Stack) to use iterators so that you can avoid repeating the iteration logic in multiple places.

Exercise 3.d: (Bonus) Try to make your iterator(s) work like those of stdlib collections, specifically:

#![allow(unused)]
fn main() {
for element in &collection { ... }      // produce shared refs (&i32 in our case), do not consume collection
for element in &mut collection { ... }  // produce mutable refs (&mut i32), do not consume collection
for element in collection { ... }       // produce items (i32), consume the collection
}