Lifetime for match variable is too pessimistic

use either::Either;
struct A;
struct B;
struct AW<'a>(&'a mut A);
impl<'a> Drop for AW<'a> {
    fn drop(&mut self) {
        println!("AW drop");
    }
}
fn cap(_: &mut A) -> Either::<AW<'_>, B> {
    unimplemented!()
}
pub fn main() {
    let mut a = A;
    let b = cap(&mut a);
    match b {
        Either::Left(a) => {
            drop(a);
            // b is destructed, and drop(a) already called
        },
        Either::Right(y) => {
            a = A;
            // b is destructed
        }
    }; // No way is the compiler going to drop b at this point in either case
}

In this piece of code, the compiler borrows b for the entire duration of match. The explanation it gives is that it needs to run drop on b at the end of the match.

However, that's not the case at all. b is partially moved in both arms, its destructor cannot be called at the end of match.

Yet the compiler still insists on borrowing b for the whole match, preventing me from modifying a in the Right arm, even if it's safe.

playground link: Rust Playground

4 Likes

Interesting, I was going to try and figure out the drop order when destruction happens but the rust compiler blocks moving out while destructing if the type implements Drop. So unless I am missing something (which tbh I probably am) this should be allowed.

I think this sort of control flow based lifetime analysis will come with the next version of the borrow checker: polonius

3 Likes

Still happens with -Z polonius though.

Anyway, this type of thing generally merits an issue report in the rust-lang/rust repo.

6 Likes