Why function call does not move the value of its params?

fn g(a: &mut i32) {
    let b = a;
    *b = 11;
    println!("{}", b);
}

fn main() {
    let a: &mut i32 = &mut 10;
    {
        let b = a;
        println!("{}", b);
    }
    println!("{}", a);
}

fn main() {
    let a: &mut i32 = &mut 10;
    g(a);
    println!("{}", a);
}

The first main compile failed with the error borrow of moved value:a``, but the second main compile successfully. I thought that calling g also move the value a. What happens here? :sweat_smile:

1 Like

Imagine the variable a has a type &'a mut i32 for some lifetime 'a. What you have in the second example is a "reborrow": the reference inside the function is &'b mut i32 with a shorter lifetime 'b that ends before the final println.

You can also change the first example to work by explicitly adding a type annotation, which allows a different lifetime:

fn main() {
    let a: &mut i32 = &mut 10;
    {
        let b: &mut i32 = a;
        println!("{}", b);
    }
    println!("{}", a);
}
4 Likes

Put simply, in certain cases when the compiler sees that a &mut T would be moved (without needing to do too much type inference first), a re-borrow is introduced instead, with the effect that g(a) becomes essentially re-written into g(&mut *a) here by the compiler. (The rule exists for convenience so you don't have to write g(&mut *a) yourself.) So a is not moved after all, instead a new reference is created pointing to the same target (and a becomes unusable for as long as that new reference lives).

Re-borrows are a “generalization” of moves in the sense that re-borrowing a &'a mut T can also produce a &'a mut T with the same lifetime which has (almost) the same effects as a move. But, as @tczajka mentioned, it can also produce a shorter-lived reference. Naturally, only in the latter case of producing a shorter-lived new reference, the original reference will ever become usable again.

The fact that re-borrowing “generalizes” moving in this sense is essential because it means that you never need to truly move a reference in such a function call like in your example. Otherwise this “automatic rewriting” would be highly inconvenient, because it prevents you from “actually” moving a &mut T into a function call (in a straightforward manner).

5 Likes

Thank you. I didn't even know re-borrowing exist.

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.