rustiteratorrust-itertools

Mutably iterate through an iterator using Itertools' tuple_windows


I'm attempting to store a series of entries inside a Vec. Later I need to reprocess through the Vec to fill in some information in each entry about the next entry. The minimal example would be something like this:

struct Entry {
    curr: i32,
    next: Option<i32>
}

struct History {
    entries: Vec<Entry>
}

where I would like to fill in the next fields to the next entries' curr value. To achieve this, I want to make use of the tuple_windows function from Itertools on the mutable iterator. I expect I can write a function like this:

impl History {    
    fn fill_next_with_itertools(&mut self) {
        for (a, b) in self.entries.iter_mut().tuple_windows() {
            a.next = Some(b.curr);
        }
    }
}

(playground)

However, it refuse to compile because the iterator Item's type, &mut Entry, is not Clone, which is required by tuple_windows function. I understand there is a way to iterate through the list using the indices like this:

    fn fill_next_with_index(&mut self) {
        for i in 0..(self.entries.len()-1) {
            self.entries[i].next = Some(self.entries[i+1].curr);
        }
    }

(playground)

But I feel the itertools' approach more natural and elegant. What's the best ways to achieve the same effect?


Solution

  • From the documentation:

    tuple_window clones the iterator elements so that they can be part of successive windows, this makes it most suited for iterators of references and other values that are cheap to copy.

    This means that if you were to implement it with &mut items, then you'd have multiple mutable references to the same thing which is undefined behaviour.

    If you still need shared, mutable access you'd have to wrap it in Rc<RefCell<T>>, Arc<Mutex<T>> or something similar:

    fn fill_next_with_itertools(&mut self) {
        for (a, b) in self
            .entries
            .iter_mut()
            .map(RefCell::new)
            .map(Rc::new)
            .tuple_windows()
        {
            a.borrow_mut().next = Some(b.borrow().curr);
        }
    }