Splitting borrow does not carry though enum match statements?

It may be by design but I'm still genuinely surprised by this behavior.

enum E {
    A(i32),
    B,
}

// Good
fn test(e: &mut E) -> &i32 {
    match e {
        E::A(v) => v,
        E::B => {
            *e = E::A(0);
            let E::A(v) = e else { panic!() };
            v
        }
    }
}

struct F {
    e: E,
}

// Breaks
fn test2(f: &mut F) -> &i32 {
    match &f.e {
        E::A(v) => v,
        E::B => {
            f.e = E::A(0); 
//cannot assign to `f.e` because it is borrowed
//`f.e` is assigned to here but it was already borrowed
            let E::A(v) = &f.e else { panic!() };
            v
        }
    }
}

// Good
fn test3(f: &mut F) -> &i32 {
    let e = &mut f.e;
    match e {
        E::A(v) => v,
        E::B => {
            *e = E::A(0);
            let E::A(v) = e else { panic!() };
            v
        }
    }
}

In test2's first match arm, I thought rustc would recognize v to be a splitting borrow of f.e. But instead, rustc seems to reason v as borrowing the entirety of f and thus spit out lifetime error.

It's another case of conditional return of a borrow, and will probably be accepted with Polonius.

1 Like

By the way, this is another version that works:

fn test4(f: &mut F) -> &i32 {
    match f.e {
        E::A(ref v) => v,
        E::B => {
            f.e = E::A(0);
            let E::A(v) = &f.e else { panic!() };
            v
        }
    }
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.