While not ideal, here is a recursive version that compiles, but requires querying mut_subtree_at
twice:
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
if self.leaf {
Ok(self)
} else {
if self.mut_subtree_at(x, y).is_some() {
// Go a level deeper
self.mut_subtree_at(x, y).unwrap().find_mut(x, y)
} else {
// Return an Err containing the lowest subtree that is sadly not a leaf
Err(self)
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
To my understanding this works because .is_some()
returns a bool
, which is an owned value and therefore Rust can prove that at the time of Err(self)
no reference is held to self
any more.
It seems that the same principle also works for the iterative solution:
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
loop {
// If we've arrived at a leaf return it as Ok
if current.leaf {
return Ok(current);
}
// Getting the subtree that contains our coordinate
if current.mut_subtree_at(x, y).is_some() {
// Go a level deeper
current = current.mut_subtree_at(x, y).unwrap()
} else {
// Return an Err containing the lowest subtree that is sadly not a leaf
return Err(current);
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
For more performance (meaning, if you don't want to call mut_subtree_at
twice), you would have to override the borrow checker as this is obviously a false positive.
Luckily, there is the polonius-the-crab
crate that is written for specifically the problem you ran into and hides the unsafe code in a safe and reliable manner.
Here is my first working version using polonius-the-crab
:
use ::polonius_the_crab::prelude::*;
use polonius_the_crab::WithLifetime;
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
type SelfRef<K> = dyn for<'lt> WithLifetime<'lt, T = &'lt mut QuadTree<K>>;
let mut current = self;
loop {
// If we've arrived at a leaf return it as Ok
if current.leaf {
return Ok(current);
}
// Getting the subtree that contains our coordinate
match polonius::<SelfRef<T>, _, _, _>(current, |current| {
current.mut_subtree_at(x, y).ok_or(())
}) {
Ok(child) => {
// Go a level deeper
current = child;
}
Err((current, ())) => {
// Return an Err containing the lowest subtree that is sadly not a leaf
return Err(current);
}
}
}
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Option<&mut Self> {
self.subtrees[self.subtree_id(x, y)].as_deref_mut()
}
}
or this version, which is a little easier to understand as it uses polonius' macros:
use ::polonius_the_crab::prelude::*;
pub struct QuadTree<T> {
x: i32,
y: i32,
dx: i32,
dy: i32,
leaf: bool,
subtrees: [Option<Box<Self>>; 4],
value: Option<T>,
}
impl<T> QuadTree<T> {
pub fn find_mut(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
while !current.leaf {
// Getting the subtree that contains our coordinate.
// If no subtree exists, return Err(current).
current = current.mut_subtree_at(x, y)?;
}
// If we are at a leaf node with the coordinate, success!
Ok(current)
}
fn subtree_id<'a>(&'a self, x: i32, y: i32) -> usize {
let mut child_id = 0;
if x >= self.x {
child_id += 1;
}
if y >= self.y {
child_id += 2;
}
child_id
}
#[inline(always)]
fn mut_subtree_at(&mut self, x: i32, y: i32) -> Result<&mut Self, &mut Self> {
let mut current = self;
polonius!(
|current| -> Result<&'polonius mut Self, &'polonius mut Self> {
if let Some(child) = current.subtrees[current.subtree_id(x, y)].as_deref_mut() {
polonius_return!(Ok(child))
}
}
);
// Return the ownership of `self` back through the `Err()` value.
// This in conjunction with the `polonius!()` macro resolves the
// ownership problem.
Err(current)
}
}