I have a gnarly borrow checker issue which I'd appreciate some help with from some veteran rustaceans. A very reduced example of my struggles is below.
The real production code is performance critical, so I don't want to be adding extra runtime overhead, but I would be willing to use unsafe, if someone could recommend the correct cure! -- or possibly use polonius-the-crab trait as per Workaround before Polonius makes it to stable if it fixes things.
But first I wanted to triple check - is my attempted usage sound? In particular, is it reasonable to believe that traverse
should pass borrow checking? (traverse_once
does pass borrow checking, so in concept I don't see why traverse
wouldn't be sound too - it just modifies N times before returning a value, instead of once).
I assume because I have a unique reference to self, I should be able to call Step
any number of times before returning a reference to mutable state under self? Does this seem right?
If so, what solutions are there in stable rust before polonius is stable? Is polonius-the-crab the best? Alternatively, is there a nice way of doing this with unsafe without just casting to a raw pointer and back?
Error
|
248 | fn traverse<'t>(&'t mut self) -> Output<'t> {
| -- lifetime `'t` defined here
249 | loop {
250 | match Self::step(&self.bytes, &mut self.offset) {
| ^^^^^^^^^^^^^^^^ `self.offset` was mutably borrowed here in the previous iteration of the loop
251 | ControlFlow::Continue(()) => {},
252 | ControlFlow::Break(output) => return output,
| ------ returning this value requires that `self.offset` is borrowed for `'t`
Code
use core::ops::ControlFlow;
type Output<'t> = &'t mut usize;
struct ExampleTraverser {
bytes: Vec<u8>,
offset: usize,
}
impl ExampleTraverser {
fn traverse_once<'t>(&'t mut self) -> ControlFlow<Output<'t>> {
match Self::step(&self.bytes, &mut self.offset) {
ControlFlow::Continue(()) => ControlFlow::Continue(()),
ControlFlow::Break(output) => return ControlFlow::Break(output)
}
}
fn traverse<'t>(&'t mut self) -> Output<'t> {
loop {
match Self::step(&self.bytes, &mut self.offset) {
ControlFlow::Continue(()) => {},
ControlFlow::Break(output) => return output,
};
}
}
fn step<'s>(bytes: &[u8], offset: &'s mut usize) -> ControlFlow<Output<'s>> {
if *offset >= bytes.len() {
ControlFlow::Break(offset)
} else {
*offset = *offset + 1;
ControlFlow::Continue(())
}
}
}