2

I have a custom iterator which reads data from a reference to a Vec in a non-contiguous manner. It will never return two references to the same memory, so I believe it is safe to implement a mutable counterpart.

However, the logic for the iterator itself is quite complex, and it would be entirely identical for the mutable version. In an attempt to avoid code duplication, I use my non-mut iterator under the hood, and then cast the result to add mutability. This works as expected, but I am unsure if it's invoking some kind of undefined behavior.

pub mod lib {
    pub struct Data {
        pub v: Vec<u64>,
    }

    impl Data {
        pub fn iter(&self) -> MyIter {
            MyIter {
                data: &self.v,
                i: 0,
            }
        }

        pub fn iter_mut(&mut self) -> MyIterMut {
            MyIterMut { iter: self.iter() }
        }
    }

    pub struct MyIter<'a> {
        data: &'a [u64],
        i: usize,
    }

    impl<'a> Iterator for MyIter<'a> {
        type Item = &'a u64;

        fn next(&mut self) -> Option<Self::Item> {
            if self.i == self.data.len() {
                return None;
            }
            let res = &self.data[self.i];
            self.i += 1;
            Some(res)
        }
    }

    pub struct MyIterMut<'a> {
        iter: MyIter<'a>,
    }

    impl<'a> Iterator for MyIterMut<'a> {
        type Item = &'a mut u64;

        fn next(&mut self) -> Option<Self::Item> {
            unsafe { std::mem::transmute(self.iter.next()) }
        }
    }
}

fn main() {
    let mut d = lib::Data { v: vec![0; 8] };

    for elem in d.iter_mut() {
        *elem = 123;
    }

    println!("{:?}", d.v);
}

Complete working example

The mutable iterator is only constructed in the iter_mut method. This means the initial value will always start as a mutable variable, due to the &mut self. It wouldn't be possible to construct this mutable iterator from an immutable variable.

In C++, often times you will use a const_cast to avoid duplicating an implementation that only varies in constness.

Is this something I can do in Rust?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
maymovo
  • 21
  • 2
  • You have defined `MyIterMut<'a>` as owning a `MyIter<'a>`. Does the `MyIter<'a>` own the `Vec` or only hold a reference to it? I'm assuming it holds a reference (based on the presence of the lifetime parameter). If that's the case then you face undefined behavior when another part of the code thinks it's getting an immutable reference to an element while code using your mutable iterator is changing it. – Matt Thomas Nov 15 '20 at 02:22
  • @MattThomas It does indeed hold a reference. At the moment, this iterator is only ever constructed within an `iter_mut()` method, meaning there is already a `&mut self`. Essentially, I am casting from `mut`, to immutable, to `mut` again. But as the initial one is mutable, I believe it's safe? Not sure. – maymovo Nov 15 '20 at 02:27
  • Your code _already_ introduces undefined behavior. Run it in Miri, [available in the playground](https://i.stack.imgur.com/VJyvb.png) and it shows you where. – Shepmaster Nov 15 '20 at 03:07

1 Answers1

1

If MyIter contains an immutable reference to the original vec, then you can't transmute, as you could have multiple instances of MyIter, leading to conflicting references. It seems like the duplicate crate could be of use here, providing an easy way to duplicate code with slight differences.

Aplet123
  • 33,825
  • 1
  • 29
  • 55
  • Could you provide a code example of how to trigger the conflicting references? I am not sure I follow. – maymovo Nov 15 '20 at 02:42
  • Here's a [complete working example in playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5aca15bc397c00e15d07f428dad9a123). – maymovo Nov 15 '20 at 02:55
  • 1
    Your answer should be moved to the duplicate [How to avoid writing duplicate accessor functions for mutable and immutable references in Rust?](https://stackoverflow.com/q/41436525/155423), along with a complete working example. – Shepmaster Nov 15 '20 at 03:08