Rustコトハジメ

プログラミング言語Rustに関する情報をお届けします。

IntoIterator implementations explored

pub trait IntoIterator {
    /// The type of the elements being iterated over.
    type Item;

    /// Which kind of iterator are we turning this into?
    type IntoIter: Iterator<Item=Self::Item>;

    fn into_iter(self) -> Self::IntoIter;
}

For-looping in Rust is like this:

for item in expression {
    code
}

and it is pretty much like in other languages but the distinction is that Rust compiler converts expression into Iterator through IntoIterator trait. In Rust language, this is a typical design pattern which takes something that can turn into something through utility trait.

IntoIterator is like Iterable in other languages like Java and python.

When expression is already and iterator it just returns self.

impl<I: Iterator> IntoIterator for I {
    type Item = I::Item;
    type IntoIter = I;

    fn into_iter(self) -> I {
        self
    }
}

Another distinction from other languages is that for-loop involves ownership and borrows in Rust. For example,

let v = !vec[1,2,3];
for item in &v

takes the borrow of expression so the item will be a borrow. If there is no ampersand, the ownership moves.

So how is this implemented?

The move version is:

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

and the borrow versions are defined as:

impl<'a, T> IntoIterator for &'a [T] {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

impl<'a, T> IntoIterator for &'a mut [T] {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;

Note: The borrow versions are also defined in Vec but as I thought at the time of this article the Vec turns into slice by deref which is why I refer to definitions in slice. But I later find that deref is not applied before IntoIterator and the blog post is here:

www.rustforbeginners.com

The Iter and IterMut are the instance of Iterator and IterMut for example

pub struct IterMut<'a, T: 'a> {
    ptr: *mut T,
    end: *mut T,
    _marker: marker::PhantomData<&'a mut T>,
}

is a structure that holds the pointer to the head and the last. The last pointer is used when iterating from back for example.

Since these two implementation only differ in mutatable or not and can share how they iterate over items the macro is used for DRY.

macro_rules! iterator {
    (struct $name:ident -> $ptr:ty, $elem:ty, $raw_mut:tt, $( $mut_:tt )*) => {
        impl<'a, T> $name<'a, T> {

        impl<'a, T> Iterator for $name<'a, T> {
            type Item = $elem;

iterator!{struct Iter -> *const T, &'a T, const, /* no mut */}
iterator!{struct IterMut -> *mut T, &'a mut T, mut, mut}