Borrow error when mutable borrow has been prolonged by immutable return from function

The following code produces the compile error "cannot borrow `*s` as immutable because it is also borrowed` as mutable":

#[derive(Debug)]
struct S {
    a: usize,
}

fn do_stuff(s: &mut S) -> &usize {
    let x = do_more_stuff(s); // Mutable borrow of `s`: `x` takes on same lifetime

    // Immutable borrow of `s`: disallowed because mutable borrow is prolonged
    // by later use of `x`
    dbg!(s as &S);

    x
}

fn do_more_stuff(s: &mut S) -> &usize {
    s.a += 1;
    &s.a
}

As far as I can see, in function do_stuff, x will have the same lifetime as s, but once the &mut s is dropped (thanks to non-lexical lifetimes), that lifetime should be only for the immutably-borrowed x. So the second immutable borrow in the dbg! call could logically be allowed.

For fun, I also tried this with rustc -Z polonius, which gave the same error.

I can workaround this with unsafe:

let x = do_more_stuff(unsafe { &mut *(s as *mut _) });

Is there a safe workaround which the compiler will accept?

Yes, just have do_more_stuff also return an immutable borrow of its argument.

#[derive(Debug)]
struct S {
    a: usize,
}

fn do_stuff(s: &mut S) -> &usize {
    let (s, x) = do_more_stuff(s);

    dbg!(s as &S);

    x
}

fn do_more_stuff(s: &mut S) -> (&S, &usize) {
    s.a += 1;
    (s, &s.a)
}
1 Like

Great! I guess that's the help the compiler needs for now. Thanks.