2

To implement a two dimensional vector type, I tried to wrap std::vec::Vec inside my own struct and implement a two dimensional mutable iterator. I know, that this is not possible in safe Rust due to this.

How to implement this in unsafe rust, safely?

The following example is the basic minimal example:

struct TwoDimVector<T> where T : Default + Clone {
    vector : std::vec::Vec<T>,
    x_dim : u32,
    y_dim : u32
} 

impl<T> TwoDimVector<T> where T : Default + Clone {
    fn new(x_dim : u32, y_dim : u32) -> TwoDimVector<T> {
       let vector = std::vec::Vec::new();
       vector.resize((x_dim * y_dim) as usize, T::default());
       TwoDimVector { vector, x_dim, y_dim } 
    }
    fn get_mut(&mut self, (x,y) : (u32, u32)) -> Option<&mut T> { 
        self.vector.get_mut((y * self.x_dim + x) as usize) 
    } 
    fn iter_mut<'a>(&'a mut self, start : (u32, u32), end : (u32, u32)) -> 
        TwoDimVectorIterMut<'a, T> {
        TwoDimVectorIterMut::new(self, start, end, self.x_dim, self.y_dim)
    } 
} 

pub struct TwoDimVectorIterMut<'a, T : 'a> where T : Default + Clone {
    vector : &'a mut TwoDimVector<T>,
    start : (u32, u32),
    end : (u32, u32),
    current : (u32, u32),
    invalid : bool
}  

impl<'a, T> TwoDimVectorIterMut<'a, T> where T : 'a + Default + Clone {
    fn new(vector : &'a mut TwoDimVector<T>, start : (u32, u32), end : (u32, 
        u32), x_dim : u32, y_dim : u32) -> TwoDimVectorIterMut<'a, T> {
        use std::cmp::{min, max};
        let start : (u32, u32) = start.into();
        let end : (u32, u32) = end.into();
        TwoDimVectorIterMut { 
            start : (min(start.0, max(x_dim, 1) - 1), 
                     min(start.1, max(y_dim, 1) - 1)), 
            end :  (min(end.0, max(x_dim, 1) - 1), 
                    min(end.1, max(y_dim, 1) - 1)),
            current : (start.0, start.1 ),  
            invalid : x_dim == 0 || y_dim == 0,
            vector : vector
        }
    }
    fn advance(&mut self) -> Option<(u32, u32)> {
        let current = self.current;
        if self.invalid || current.0 > self.end.0 || current.1 > self.end.1 {
            return None
        }
        if self.current.0 == self.end.0 {
            self.current.0 = self.start.0;
            self.current.1 += 1; 
        } else {
            self.current.0 += 1;
        }    
        return Some(current);
    } 
}

impl<'a, T> Iterator for TwoDimVectorIterMut<'a, T> where T : Default + Clone 
{
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        let point = self.advance();
        if let Some(point) = point {
            unsafe {
              self.vector.get_mut(point) // what to write here??
            }
        } else {
            None
        }
    }
}    

Link to Rust playground

kilian
  • 79
  • 4
  • I added a complete, minimal, running example. With that said: it must be possible to write such an iterator in rust, because iter_mut() of std::vec::Vec ist exactly such. I saw other libraries, like ndarray, which also provide such an iterator. So it must be possible to implement one. Unfortunately the source code of these libraries is not easy to understand. – kilian Feb 19 '18 at 11:58
  • This should not be a duplicate, as totally other things go on (also the error is completely different). The guy in the other thread wants to change to signature of iterators, which I do not do. I simply "wrap" std::vec::Vec's iter_mut – kilian Feb 19 '18 at 12:48
  • 1
    [Here is the solution](https://play.rust-lang.org/?gist=4c51594fb1b0cb182a4df61e61103db8&version=stable) from the duplicate applied to your case. You can return the internal collection's iterator type. – E_net4 Feb 19 '18 at 13:31
  • Yeah. That seems more likely. As the original thread is from 2014 and Rust at 2014 has not much to do with Rust 2017, this question is still worth asking. Also this strides::MutIter library mentioned is not actively maintained anymore. – kilian Feb 19 '18 at 15:57
  • 1
    If you check the review dates, you will see that the question has been kept up to date. Yes, Rust brought new features to the table in 3 years, but that has not changed how the borrow checker works in this situation. Didn't the solution as presented on the playground solve the problem? I do not believe is not worth replicating the same solution here. – E_net4 Feb 19 '18 at 16:23
  • Sorry: 1. the "solution" of the other thread does not help. I need a two dimensional iterator. Exposing the inner iter_mut() does not help here at all, because I need my own iterator, which I cannot build using rust. 2. A safe solution using "unsafe" would be cool too I guess I stripped my example too much down. It was enough stripped to show what the compiler error is, but too much stripped to actually show what I want. Exposing the inner iter_mut(), is, of course, not what I want. – kilian Feb 20 '18 at 08:49

0 Answers0