1
votes

I'm having a problem with the code below. The immutable iterator works fine, but the mutable one gives me the following error:

cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements.

Is it possible to somehow fix this error without using unsafe rust and sticking to the Iterator trait?

Link to the playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ae0b3f4c5c7749b130fe0b0142beb9e7

Code:

struct Collection<T, const S: usize> {
    data: Vec<T>
}

struct CollectionIterator<'a, T, const S: usize> {
    item_index: usize,
    collection: &'a Collection<T, S>
}

struct CollectionIteratorMut<'a, T, const S: usize> {
    item_index: usize,
    collection: &'a mut Collection<T, S>
}

impl<T: Clone, const S: usize> Collection<T, S> {
    fn new(num_items: usize, default_value: T) -> Collection<T, S> {
        Collection {
            data: vec![default_value; num_items * S]
        }
    }
    fn iter(&self) -> CollectionIterator<T, S> {
        CollectionIterator {
            item_index: 0,
            collection: self
        }
    }
    fn iter_mut(&mut self) -> CollectionIterator<T, S> {
        CollectionIterator {
            item_index: 0,
            collection: self
        }
    }
}

impl<'a, T, const S: usize> Iterator for CollectionIterator<'a, T, S> {
    type Item = &'a [T];
    fn next(&mut self) -> Option<Self::Item> {
        if self.item_index < self.collection.data.len() {
            self.item_index += S;
            Some(&self.collection.data[self.item_index - S .. self.item_index])
        } else {
            None
        }
    }
}

impl<'a, T, const S: usize> Iterator for CollectionIteratorMut<'a, T, S> {
    type Item = &'a mut [T];
    fn next(&mut self) -> Option<Self::Item> {
        if self.item_index < self.collection.data.len() {
            self.item_index += S;
            Some(&mut self.collection.data[self.item_index - S .. self.item_index])
        } else {
            None
        }
    }
}

fn main() {
    let mut c: Collection<f64, 3> = Collection::new(5, 0.0);
    for x in c.iter_mut() {
        x[0] = 100.0;
    }
    for x in c.iter() {
        println!("{} {} {}", x[0], x[1], x[2]);
    }
}

Error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/main.rs:52:23
   |
52 |             Some(&mut self.collection.data[self.item_index - S .. self.item_index])
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 49:13...
  --> src/main.rs:49:13
   |
49 |     fn next(&mut self) -> Option<Self::Item> {
   |             ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:52:23
   |
52 |             Some(&mut self.collection.data[self.item_index - S .. self.item_index])
   |                       ^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 47:6...
  --> src/main.rs:47:6
   |
47 | impl<'a, T, const S: usize> Iterator for CollectionIteratorMut<'a, T, S> {
   |      ^^
note: ...so that the types are compatible
  --> src/main.rs:49:46
   |
49 |       fn next(&mut self) -> Option<Self::Item> {
   |  ______________________________________________^
50 | |         if self.item_index < self.collection.data.len() {
51 | |             self.item_index += S;
52 | |             Some(&mut self.collection.data[self.item_index - S .. self.item_index])
...  |
55 | |         }
56 | |     }
   | |_____^
   = note: expected `Iterator`
              found `Iterator`
1

1 Answers

0
votes
    fn next(&mut self) -> Option<Self::Item> {
        if self.item_index < self.collection.data.len() {
            self.item_index += S;
            Some(&mut self.collection.data[self.item_index - S .. self.item_index])
        } else {
            None
        }
    }

You have written code that will produce a series of mutable references to different parts of self.collection. But the compiler (borrow checker) does not do any analysis of the index arithmetic to determine that they are in fact non-overlapping. So, this code is rejected. In general, iterators producing mutable references into a collection cannot be written without unsafe code, because the only kind of non-overlapping references the borrow checker understands are distinct struct/tuple fields.

However, you don't need to write unsafe code to solve this particular problem, because the iterator std::slice::ChunksMut, returned by <[T]>::chunks_mut, already contains the implementation you need. The non-mutable chunks() can replace your other iterator implementation, too.

impl<T: Clone, const S: usize> Collection<T, S> {
    fn new(num_items: usize, default_value: T) -> Collection<T, S> {
        Collection {
            data: vec![default_value; num_items * S]
        }
    }
    fn iter(&self) -> impl Iterator<Item = &[T]> + '_ {
        self.data.chunks(S)
    }
    fn iter_mut(&mut self) -> impl Iterator<Item = &mut [T]> + '_ {
        self.data.chunks_mut(S)
    }
}