This question already has an answer here:
I am implementing matrices in Rust. The code is adapted for the example, but there might be minor mistakes:
#[derive(Debug, PartialEq)]
pub struct Matrix<T> {
inner: Vec<Vec<T>>,
}
impl<T> Matrix<T> {
pub fn dim(&self) -> (usize, usize) {
if self.inner.len() == 0 {
(0, 0)
} else {
(self.inner.len(), self.inner[0].len())
}
}
}
I want to have the ability to get quadrants of the matrix:
+----+----+
| Q1 | Q2 |
+----+----+
| Q3 | Q4 |
+----+----+
I introduced the Slice
and SliceMut
structures to borrow a part of the matrix:
pub struct Slice<'a, T: 'a> {
matrix: &'a Matrix<T>,
start: (usize, usize),
end: (usize, usize),
}
pub struct SliceMut<'a, T: 'a> {
matrix: &'a mut Matrix<T>,
start: (usize, usize),
end: (usize, usize),
}
Now I want to implement two functions:
quadrants
- to get a tuple of four slicesquadrants_mut
- to get a tuple of four mutable slices
I cannot mutably borrow one matrix several times in quadrants_mut
:
fn quadrants_mut<'a, T>(matrix: &'a mut Matrix<T>) -> (SliceMut<'a, T>, SliceMut<'a, T>, SliceMut<'a, T>, SliceMut<'a, T>) {
let (rows, cols) = matrix.dim();
let mid_rows = rows / 2;
let mid_cols = cols / 2;
let a = SliceMut { matrix: matrix, start: (0, 0), end: (mid_rows, mid_cols) };
let b = SliceMut { matrix: matrix, start: (0, mid_rows), end: (mid_cols, cols) };
let c = SliceMut { matrix: matrix, start: (mid_rows, rows), end: (0, mid_cols) };
let d = SliceMut { matrix: matrix, start: (mid_rows, rows), end: (mid_cols, cols) };
(a, b, c, d)
}
When I try to compile that, I have an error:
error[E0499]: cannot borrow `*matrix` as mutable more than once at a time
--> src/matrix/slice.rs:62:13
|
59 | let a = SliceMut { matrix: matrix, start: (0, 0), end: (mid_rows, mid_cols) };
| ------ first mutable borrow occurs here
...
60 | let b = SliceMut { matrix: matrix, start: (0, mid_rows), end: (mid_cols, cols) };
| ^^^^^^ second mutable borrow occurs here
...
66 | }
I am trying to mutably borrow a matrix four times. How should I change the code to make it compile?
What you want to do, is definitely possible. However it's hard. Really hard.
Luckily, I'll only show how to do the easy part.
If you look at how
split_at_mut
is implemented, you can notice it requires you to create a parallel structure that mimics return value ofquadrants_mut
(i.e.SliceMut
):Note the similarity between these two structures. If at any point they diverge, your
mem::transmute
will either stop working, or your safe code will experience segfaults.Then we create a method that transmutes
MatrixRaw
intoSliceMut
.As a last step, we add an
unsafe
block toquadrant_mut
:Link to playground
HARD PART: Here comes the hard part - making sure your methods, and iterators, don't accidentally invalidate your data and invariants. This is extremely hard to achieve in the
Matrix
case.Why? Well, because there isn't a nice way to say to your data, "don't touch these parts" like you can with an array. With an array, you just offset your data and you're pretty much good to go. But a
Matrix
? It's not impossible, but I suspect I don't know of a way that doesn't introduce performance penalties.Safe Rust does not allow to have multiple mutable bindings at once. This is implemented with checking the binding type (whether it is mutable) and counting. The compiler is not so smart to understand your intentions fully and so it can be told that the slices you use will never intersect. By having multiple mutable references in your code, even to different parts of the data, you still violate the rule.
As a solution it is possible to have one reference and indexes which will give you quadrant data: its
begin
andend
indexes, or justbegin
andcount
:playground
As for
split_at_mut
, it uses unsafe Rust and implemented as the following: