This crate provides the maybe_borrow! and try_maybe_borrow! macros for conditionally returning borrowed data, without losing access to the original borrow in the non-returning case.
Preface
This crate is extremely heavily inspired by
polonius-the-crab by
@danielhenrymantilla,
so please check that out!
Motivation
A current borrow checker limitation assumes that when borrowed data is conditionally returned, that data is considered borrowed for the lifetime of the function.
Following are examples of code that are affected by this limitation.
Case: Downcast fallback
Let's say you have &mut dyn Any reference that might point to a Vec<T> or Box<[T]>.
If it does refer to a value of one of those types, you should be able to a &mut [T] reference to its data. This following code looks like it should do the trick:
use Any;
Error diagnostic
The above code produces the following errors:
error[E0499]: cannot borrow `*src` as mutable more than once at a time
--> src\lib.rs:10:22
|
5 | fn downcast_slice_mut<T: 'static>(src: &mut dyn Any) -> Result<&mut [T], &mut dyn Any> {
| - let's call the lifetime of this reference `'1`
6 | if let Some(v) = src.downcast_mut::<Vec<T>>() {
| --- first mutable borrow occurs here
7 | return Ok(&mut *v)
| ----------- returning this value requires that `*src` is borrowed for `'1`
...
10 | if let Some(b) = src.downcast_mut::<Box<[T]>>() {
| ^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*src` as mutable more than once at a time
--> src\lib.rs:14:16
|
5 | fn downcast_slice_mut<T: 'static>(src: &mut dyn Any) -> Result<&mut [T], &mut dyn Any> {
| - let's call the lifetime of this reference `'1`
6 | if let Some(v) = src.downcast_mut::<Vec<T>>() {
| --- first mutable borrow occurs here
7 | return Ok(&mut *v)
| ----------- returning this value requires that `*src` is borrowed for `'1`
...
14 | return Err(src)
| ^^^ second mutable borrow occurs here
error: aborting due to 2 previous errors
After attempting to downcast src to Vec<T> fails, src is considered borrowed for the remainder of the function because the conditional return.
Solution using maybe_borrow!
We can place each conditional return in a maybe_borrow! invocation, which ensures that src is still usable in the event that the downcast fails and a borrowed value is not returned.
use Any;
use *;
Case: The lending iterator
/// Returns the next item from `iter` that satisfies the given predicate
Error diagnostic
The above code produces the following error:
error[E0499]: cannot borrow `*iter` as mutable more than once at a time
--> src/lending_iterator.rs:15:14
|
10 | fn next_filtered<'iter, I: for<'x> LendingIterator<'x>>(
| ----- lifetime `'iter` defined here
...
15 | match iter.next() {
| ^^^^ `*iter` was mutably borrowed here in the previous iteration of the loop
16 | Some(ref item) if !predicate(item) => {},
17 | out => return out,
| --- returning this value requires that `*iter` is borrowed for `'iter`
Solution with maybe_borrow! or try_maybe_borrow!
use *;
type Item<'iter, I> = Item;
/// Returns the next item from `iter` that satisfies the given predicate
Working with pinned data
The maybe_borrow! and try_maybe_borrow! macros work on Pin<&mut T> references in addition to plain &mut T references.
use *;
/// Returns the next item from `iter` that satisfies the given predicate
Operating on multiple references
Multiple references can be used as long as they have the same lifetime:
use ;
use maybe_borrow;
/// Finds the first key in `keys` that can be found in `map` and returns a mutable reference to its
/// value, or `None` if none of the keys exist in `map`.
Notes
As mentioned above, this crate is largely based on
polonius-the-crab.
If you don't need features like pinned references or multiple references, polonius-the-crab is better documented, better tested, and likely the better choice.