Return either borrow of self or a borrow of a self's element

Hi,

I was trying to do something like this:

fn first_zero_or_self_bad<'a>(xs: &'a mut Vec<u8>)
-> Either<&'a mut Vec<u8>, &'a mut u8>
{
    if let Some(x) = xs.iter_mut().find(|x| **x == 0)
        { Either::Right(x) }
    else 
        { Either::Left(xs) }
}

And variants with different lifetime signatures, e.g.

fn first_zero_or_self_bad<'b, 'a: 'b>(xs: &'a mut Vec<u8>)
-> Either<&'a mut Vec<u8>, &'b mut u8>

But the code just cannot be compiled because of different yet related lifetimes of xs and xs.iter_mut(). I think that it is quite a safe thing to do, but I understand that Rust does not reason about Either<A, B> with a knowledge that A and B will be mutually exclusive and that's why I have to persuade it.

I've came up with a workaround:

struct Ptr<'a, T>{x: *mut T, life: PhantomData<&'a ()>}

fn first_zero_or_self<'a>(xs: &'a mut Vec<u8>)
-> Either<&'a mut Vec<u8>, Ptr<'a, u8>>
{
    if let Some(x) = xs.iter_mut().find(|x| **x == 0)
        { Either::Right(Ptr{x: &mut *x, life: PhantomData}) }
    else
        { Either::Left(xs) }
}

My questions are:

  1. Is there some other more elegant solution or more standard practice that I'm missing?
  2. Is my workaround a safe thing to do? (I will not publish the fields of the Ptr struct and the published functions that use the Ptr will not leak the pointer)

Let's say I need Vec to implement a trait implemented by other structures, so the type signature of the function should remain in the form fn(&mut Self) -> Either<Self::A<'_>, Self::B<'_>>.

In advance, thank you very much for your replies and opinions.

Here's my playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6ae4d5bd0e8cb86509df6771e338c37f

This is a known limitation of the current borrow checker. The next-generation borrow checker Polonius will be able to compile your original code. You can try the experimental version in the nightly toolchain by compiling with -Zpolonius (in your rustc arguments or $RUSTFLAGS).

For now, one possible workaround is to use indexing:

fn first_zero_or_self_bad(xs: &mut Vec<u8>)
-> Either<&mut Vec<u8>, &mut u8>
{
    if let Some(i) = xs.iter().position(|x| *x == 0)
        { Either::Right(&mut xs[i]) }
    else 
        { Either::Left(xs) }
}
2 Likes

Understood. Thank you for your explanation :slight_smile: looking forward for Polonius.