How to implement Iterator with Item = &mut T

I am trying to implement the Iterator trait:

impl<'a, T: MatrixView> Iterator for MatrixViewIterMut<'a, T> {
    type Item = &'a mut T::Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.col == self.dim_col { 
            self.col = 0; 
            self.row += 1;
        }

        if self.row >= self.dim_row {
            return None;
        }

        let result = unsafe { Some(self.data.get_unchecked_mut(self.row, self.col)) };
        self.col += 1;
        result
    }
}

However, this code failed to compile with the following message:

error: lifetime may not live long enough
  --> src\views\iters.rs:83:9
   |
68 | impl<'a, T: MatrixView> Iterator for MatrixViewIterMut<'a, T> {
   |      -- lifetime `'a` defined here
...
71 |     fn next(&mut self) -> Option<Self::Item> {
   |             - let's call the lifetime of this reference `'1`
...
83 |         result
   |         ^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

For what I understand, the lifetime of &mut self might be shorter than 'a and result is bound to the same lifetime as self so this is invalid.

I'm having a hard time coming up with a solution for this. I tried to change the next function so self also has 'a lifetime but it will no longer implement Iterator correctly.

Related code:

pub trait MatrixView {
    type Item;
    unsafe fn get_unchecked(&self, row: usize, col: usize) -> &Self::Item;
    unsafe fn get_unchecked_mut(&mut self, row: usize, col: usize) -> &mut Self::Item;
    fn dimension(&self) -> (usize, size);

    // -- snip --
}

pub struct MatrixViewIterMut<'a, T: MatrixView> {
    data: &'a mut T,
    row: usize,
    col: usize,
    dim_row: usize,
    dim_col: usize
}

Usually, iterators yielding mutable references cannot be implemented from scratch using safe code. Your best bet is to wrap and defer to already-implemented (e.g. std) iterators.

Also note that unsafe doesn't mean turning off the borrow checker (or any other check available in safe code). A &mut in unsafe has to obey the same rules as a &mut in safe code. All that unsafe code allows you to perform are a few, inherently unsafe actions, such as dereferencing raw pointers or calling unsafe functions.

1 Like

You will have to use transmute to override the lifetimes. The iterator requires that every mutable item returned is unique, but the borrow checker is incapable of proving that from the implementation.

&mut * { thing as *mut _ } is better than transmuting IMO.

I wouldn't be surprised if it was still unsound either, depending on the limitations imposed on the get_unchecked_mut method. (I'm assuming something like the below.)

// What are the requirements on the trait implementor?
unsafe fn get_unchecked_mut(&mut self, _: usize, _: usize) -> &mut Self::Item;

You could make implementors provide there own iterators.

type IterMut<'a>: Iterator<Item = &'a mut Self::Item> where Self: 'a;
fn iter_mut(&mut self) -> Self::IterMut<'_>;

Edit: An example to run Miri against.

3 Likes

Also, as far as I'm aware, this place is for requirements on the caller of get_unchecked_mut, whereas requirements on the trait implementor of the MatrixView impl are supposed to be documented on the trait itself which would have to become an unsafe trait, too:

// What are the requirements on the trait implementor?
unsafe trait MatrixView { … }

I always thought it was an ambiguous situation, and thus a place for requirements on both the implementor and the caller. The implementer has to write unsafe and the entire function body is an unsafe block [1]. Because the body is unsafe, clearly the implementor has to uphold whatever constraints are necessary. But Rust doesn't force you to make the trait unsafe [2], so those constraints must be tied to the method.

I believe that's what this is saying too.


  1. so far ↩︎

  2. or supply a default body, etc ↩︎

This may change if 2316-safe-unsafe-trait-methods - The Rust RFC Book / 3245-refined-impls - The Rust RFC Book gets implemented and stabilized.

2 Likes

For now, the requirement for get_unchecked_mut is row, and col must not be out of bounds of the matrix dimensions. I'm thinking of dropping support for mutable iterators because my idea for MatrixView is performing calculations on the MatrixView is lazy. Matrix transposition is fine but when I implement matrix addition, I cannot return references in get_unchecked or get_unchecked_mut. My idea currently is changing get_unchecked to return Self::Item directly and drop get_unchecked_mut

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.