I have some Rust code that takes a reference and returns a Result
that holds a reference in the Ok
case and doesn't in the Err
case. (This is simplified; in reality the output is a struct holding multiple references to the data and the error is something fancier.)
fn try_subslice(data: &[u8]) -> Result<&[u8], ()>
I have a struct that holds a Vec<u8>
. I want to call try_subslice
and either return the successful value or increase the size of the internal Vec, passing along the error.
pub struct Owner(Vec<u8>);
impl Owner {
pub fn subslice_or_grow(&mut self) -> Result<&[u8], ()> {
let result: Result<&[u8], ()> = try_subslice(&self.0);
match result {
Ok(value) => Ok(value),
Err(_) => {
self.0.resize(self.0.len() * 2, 0);
Err(())
}
}
}
}
The full example is on the Rust Playground.
When I do this, the compiler complains that the mut borrow required by resize
overlaps with the non-mut borrow made when calling try_subslice
. I know that by the time the Err
branch of the match
is taken, the non-mut borrow is definitely over, because there is no way for ()
to hold a reference.
error[E0502]: cannot borrow `self.0` as mutable because it is also borrowed as immutable
--> src/lib.rs:10:17
|
4 | pub fn subslice_or_grow(&mut self) -> Result<&[u8], ()> {
| - let's call the lifetime of this reference `'1`
5 | let result: Result<&[u8], ()> = try_subslice(&self.0);
| ------- immutable borrow occurs here
...
8 | Ok(value) => Ok(value),
| --------- returning this value requires that `self.0` is borrowed for `'1`
9 | Err(_) => {
10 | self.0.resize(self.0.len() * 2, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
If I modify the code to not return the value
(e.g. Ok(value) => panic!()
), then it compiles. Also, if I modify try_subslice
to copy instead of borrow, then it also compiles.
When I squint, RFC 2094 Case #3 looks pretty similar to my situation. In their case, they were able to resolve it by adding a separate check to see which case they are in ahead of time. I don't see a way to do that without redesigning try_subslice
(or calling it twice, once just to see which scenario we're in and otherwise discarding the result).
I have two questions:
- Am I correct that this is a gap in the current NLL borrow checker and that the code is fundamentally sound?
- Is there a sound way to work around this limitation (potentially using
unsafe
) without redesigning thetry_subslice
interface or double-calling?
Similar questions: