12
IntoIter
Rust's iteration protocol is a single trait, Iterator, with one required method: fn next(&mut self) -> Option<Self::Item>. To iterate, you build a state-machine struct, implement Iterator for it, and yield items from next. There's no yield keyword; you write the state machine yourself.
- There are three iter conventions in Rust, by the kind of access they grant:
into_iterconsumes the collection and yields owned values,iterborrows it shared and yields&T,iter_mutborrows it exclusively and yields&mut T. Standard collections all provide all three; we will too.// for x in &list // calls list.iter() -> &T // for x in &mut list // calls list.iter_mut() -> &mut T // for x in list // calls list.into_iter() -> T (consumes list) -
IntoIteris the easiest of the three because we already wrotepop. Wrap the list in a tuple struct, andnextis justself.0.pop(). The list shrinks as we go; when it's empty,popreturnsNoneand the iterator naturally terminates.pub struct IntoIter<T>(List<T>); impl<T> List<T> { pub fn into_iter(self) -> IntoIter<T> { IntoIter(self) } } impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { self.0.pop() } } - Now
for x in list { ... }works. Theforloop callsIntoIterator::into_iter, which we've effectively provided via the inherent method (the trait impl forfor-loop interop is mechanical and we'll add it later). The list is moved into the iterator and dropped element by element.let mut list = List::new(); list.push(1); list.push(2); list.push(3); let mut it = list.into_iter(); assert_eq!(it.next(), Some(3)); assert_eq!(it.next(), Some(2)); assert_eq!(it.next(), Some(1)); assert_eq!(it.next(), None);