I'm writing a library which requires two structs to have mutable access to different parts of the same Vec. The split I want is a not [0..n]
and [n..]
, so I cannot use split_at_mut
. I came up with the following reduced test case that I want to know is correct or not.
Essentially the returned structs mutably borrow the Vec without actually storing a mutable reference to it, using PhantomData
. The 2 structs then never access the same memory locations -- one accesses/modifies only even indices and the other only odd. Is this correct?
use std::marker;
struct Evens<'a, T: 'a> {
ptr: *mut Vec<T>,
marker: marker::PhantomData<&'a mut Vec<T>>,
}
struct Odds<'a, T: 'a> {
ptr: *mut Vec<T>,
marker: marker::PhantomData<&'a mut Vec<T>>,
}
impl<'a, T: 'a> Evens<'a, T> {
fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index % 2 == 0 {
unsafe { (*self.ptr).get_mut(index) }
} else {
None
}
}
}
impl<'a, T: 'a> Odds<'a, T> {
fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index % 2 == 1 {
unsafe { (*self.ptr).get_mut(index) }
} else {
None
}
}
}
fn split<'a, T>(vec: &'a mut Vec<T>) -> (Odds<'a, T>, Evens<'a, T>) {
(Odds {
ptr: vec as *mut _,
marker: marker::PhantomData,
},
Evens {
ptr: vec as *mut _,
marker: marker::PhantomData,
})
}
fn main() {
let mut vec = vec![1, 2, 3];
let (mut odds, mut evens) = split(&mut vec);
assert_eq!(evens.get_mut(0), Some(&mut 1));
assert_eq!(evens.get_mut(1), None);
assert_eq!(odds.get_mut(0), None);
assert_eq!(odds.get_mut(1), Some(&mut 2));
// vec[1]; // cannot borrow `vec` as immutable because it is also borrowed as mutable
{
let _a = evens.get_mut(0);
// let _b = evens.get_mut(0); // cannot borrow `evens` as mutable more than once at a time
}
}
The library will only allow creating an instance of Evens
and Odds
through the split
function.