How to finish borrow inside a match arm?

The short form of match <enum> moves the <enum> value. In my case such <enum> uses borrowing only in some variants. I supposed that moving the <enum> inside match 'splits' the variants and so other variants are 'dropped' in some kind. I wish to use the origin of borrow in a match arm, which variant does not requires borrowing. I thought that non-lexical lifetimes play by my side, but they don't. So, how to finish borrow inside a match arm?
An example:

#[derive(Debug)]
enum OkKind<'a> {
    Ok1(&'a u32),
    Ok2,
}

fn my_do<'a> (s: &'a mut S) -> Result<OkKind<'a>, ()> {
    let res = || -> Result<OkKind<'_>, ()> {
                  s.try_get() 
              }();
    match res { // I supposed here we move the 'res', so its variants are in some kind 'splitted'
        Ok(r) => { // use borrowing as expected
            Ok(r)
        },
        Err(e) => { // 'e' does not depend on any borrows, how to finish the 'res' borrow here?
            s.revert_mut(4); // to make &mut self available
            Err(e)
        }
    }
}

a fuller example

I might be wrong in rust usage from the beginning. In the origin spaghetti-code S plays a role of a command chain, so I use closure to try series of commands to revert them in case of error. Where am I wrong and how shall I act?

Thanks

This is a case that Rust's current borrow checker cannot handle. To it, a mutable borrow that might be returned is the same as a borrow that is always returned — its lifetime extends from where it was created (try_get) to the end of the function, unconditionally. You're not returning a mutable reference, but that doesn't matter because 'a is still the lifetime of a mutable borrow.

The general solution to this problem is: don't take a mutable borrow until you know for certain that you are going to return it. In your case, you'll have to change S::try_get() to enable this, either by splitting it into a "check the condition" operation (that doesn't return any borrows from &mut self) followed by a “get the borrow I'm going to return” operation, or by combining the functionality of revert_mut() into try_get() so that you don't have to do a second mutation operation in the error branch.

2 Likes

This is the issue for the flow-aware NLL feature needed in case you'd like to follow it; here's a modification of your example that works with the (incomplete) next-gen borrow checker (Polonius).

One point of interest is that you also need the new (edition 2021) closure capturing semantics as well. If you're really curious why, you can read an explanation here (if you "ask for it" :slightly_smiling_face:).

2 Likes

Thank you for the links, I already use 2021 and hope Polonius will eliminate the issue. As a workaround I will adapt the @kpreid suggestion to borrow again, after the lambda. It works and now I think I could find it myself)
Looking forward to see Polonius, you showed, in stable