Why am I allowed to have multiple &mut refs in nested functions (Rust)?

Viewed 334

I'm new to rust, and am wondering why the following code doesn't result in a: cannot borrow val as mutable more than once at a time error. It seems like by the time I've reached the second_layer function, I should have three separate references to the same original val variable:

val_ref in the main function body

val_ref2 in the first_layer function body

val_ref3 in the second_layer function body

Any help would be appreciated!

fn first_layer(val_ref2: &mut String)
{
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    second_layer(val_ref2);
}

fn second_layer(val_ref3: &mut String)
{
    *val_ref3 = String::from("second_layer");
    println!("{}", val_ref3);

}

fn main()
{
    let mut val = String::from("asdf");
    let val_ref: &mut String = &mut val;

    first_layer(val_ref);

    println!("{}", val_ref);

}

Thanks,

1 Answers

References in a function calling another function temporarily do not exist for the duration of the function call.

Identifiers do not automatically live on in functions called further down the stack. Just because one function calls another does not mean that the callee should have access to the caller's local variables. You would get a not found in this scope if you tried.

What you can (try to) do is create a closure to capture the caller's environment and then reuse one of the caller's variables. But you will find that the compiler complains if you break the borrowing rules.

fn first_layer(val_ref2: &mut String) {
    // println!("{}", val); // Cannot use val from main!
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    (|val_ref3: &mut String| {
        *val_ref3 = String::from("second_layer");
        println!("{}", val_ref3);
        println!("{}", val_ref2); // This is not allowed
    })(val_ref2);
}

fn main() {
    let mut val = String::from("asdf");
    let val_ref: &mut String = &mut val;

    first_layer(val_ref);

    println!("{}", val_ref);
}

Another way to think about it is with inlining. If you inline your function second_layer into first_layer you get:

fn first_layer(val_ref2: &mut String)
{
    *val_ref2 = String::from("first_layer");
    println!("{}", val_ref2);
    *val_ref2 = String::from("second_layer");
    println!("{}", val_ref2);
}

This is totally fine!

So then the code with the last two lines abstracted to its own function should also be totally fine.

Related