Hmm, the doubly mutually-reborrowing pattern was indeed challenging for ::polonius-the-crab
; but that crate is nonetheless capable of yielding a compiling solution that requires no unsafe
from the caller:
fn next_frame<'r>(&'r mut self) -> (&'r [u8], &'r [u8]) {
use ::polonius_the_crab::*;
// outer polonius, we assume the `r_buf` side has been sorted out.
match polonius::<_, _, ForLt![<'left> = (&'left [u8], &'r [u8])]>(
&mut self.left_stream,
// inner polonius; we have to sort out the `r_buf` stuff. 'left
|left_stream| match polonius::<_, _, ForLt![<'right> = (Option<&'_ [u8]>, &'right [u8])]>(
&mut self.right_stream,
|right_stream| {
let l_buf = left_stream.next();
let r_buf = right_stream.next();
// Sometimes, we want to skip this frame based on some condition that
// can only be calculated when we have access to the buffer.
// In the camera case, this will be when a frame is too old compared to the other camera.
let delta = l_buf[0] - r_buf[0];
match DeltaRes::from_delta(delta)
// polonius logic: we are within the *inner* `polonius()` call, to determine
// whether we are outputting anything `right_stream`-dependent, such as `r_buf`,
// in which case we have to be `PoloniusResult::Borrowing` (and allowed to use
// `r_buf`); otherwise we return `PoloniusResult::Owned` with everything but
// `r_buf`/`right_stream`-related stuff, and the *caller* will receive the
// original `right_stream` in the `input_borrow` spot.
{
DeltaRes::Synchronized => {
// Cameras are synchronized, and we do not need to update any of them
// (l_buf, r_buf)
// \-> mentions r_buf: `Borrowing`
PoloniusResult::Borrowing((Some(l_buf), r_buf))
}
DeltaRes::LeftLagging => {
// Left is lagging behind, update it
// (self.left_stream.next(), r_buf)
// \-> mentions r_buf: `Borrowing`
// + but we'll also need to convey, to the caller, that
// we're not interested in `l_buf` anymore.
// Hence the `Option`.
PoloniusResult::Borrowing((None, r_buf))
}
DeltaRes::RightLagging => {
// Right is lagging behind, update it
// (l_buf, self.right_stream.next())
// \-> we want to use `right_stream`, so we skip any further mentions
// of `r_buf`, and return something `Owned`.
PoloniusResult::Owned {
input_borrow: Placeholder, // <- please give me back `right_stream`
value: l_buf, // "pass-through" companion payload.
}
}
}
}
)
// polonius logic: we are within the *outer* `polonius()` call, receiving
// the output of the inner `polonius`: we'll either have a `return`-able `r_buf`
// (friendly lifetime in it), or access to the `input_borrow` to the `right_stream`,
// so as to query again.
// We still have to determine whether we are outputting anything `left_stream`-dependent,
// such as `l_buf`;
/* .match */ {
/*Inner*/PoloniusResult::Borrowing((Some(l_buf), r_buf)) => {
/*Outer*/PoloniusResult::Borrowing(
(l_buf, r_buf)
)
},
// (self.left_stream.next(), r_buf)
/*Inner*/PoloniusResult::Borrowing((None, r_buf)) => {
/*Outer*/PoloniusResult::Owned {
input_borrow: Placeholder, // <- please give me back `left_stream`
value: r_buf,
}
},
// (l_buf, self.right_stream.next())
/*Inner*/PoloniusResult::Owned {
input_borrow: right_stream, // <- we got `right_stream` back!
value: l_buf,
} => /*Outer*/PoloniusResult::Borrowing(
(l_buf, right_stream.next())
),
}
)
// Outside the outer polonius: time to just inject `left_stream.next()` wherever we
// receive the `input_borrow: left_stream` back:
/* .match */ {
PoloniusResult::Borrowing((l_buf, r_buf)) => (
l_buf, r_buf,
),
// (self.left_stream.next(), r_buf)
PoloniusResult::Owned { value: r_buf, input_borrow: left_stream } => (
left_stream.next(), r_buf,
),
}
}