3

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,

rico5678
  • 33
  • 3
  • 3
    This is due to implicit reborrow. Please take a look at this [post](https://stackoverflow.com/questions/62960584/do-mutable-references-have-move-semantics). – Joe_Jingyu Dec 13 '21 at 05:46
  • 2
    @Joe_Jingyu It's not reborrowing that allows nested references to exist, it's the other way around - it's the nested references that allow reborrowing (explicit _or_ implicit). This question seems to ask why aliasing of nested references is allowed to begin with, and the linked answer doesn't address that. – user4815162342 Dec 13 '21 at 08:30
  • 1
    Thanks for your comments, @user4815162342. It's unclear to me why you think the linked post doesn't address the case. Isn't it because of reborrow that `val-ref` is not moved and still accessible after the call to `first_layer` in `main`? – Joe_Jingyu Dec 13 '21 at 09:14
  • 1
    @Joe_Jingyu Because the asker here is asking how is it that nested references are possible _to begin with_, regardless of implicit reborrowing. In other words, why does `let mut i = 0i32; let r1 = &mut i; let r2 = &mut *r1` compile when it obviously creates aliased mutable references to `i`? The discussion of implicit reborrows doesn't cover that because it just explains how implicit reborrows prevent references from being moved by creating nested references instead. It doesn't explain why nested inner references are allowed to alias data from outer ones. – user4815162342 Dec 13 '21 at 09:23
  • 1
    @user4815162342 I see your point. However, I don't know if [RFC#2094](https://rust-lang.github.io/rfcs/2094-nll.html) is a good documentation on the motive of reborrow. If you know one more suitable for beginners. I would love to read too. Thanks. – Joe_Jingyu Dec 13 '21 at 09:58
  • 1
    @Joe_Jingyu Unfortunately I don't, other than the SO answers you're already aware of. Improving the documentation for reborrowing is the target of [this issue](https://github.com/rust-lang/reference/issues/788). – user4815162342 Dec 13 '21 at 10:11

1 Answers1

0

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.

hkBst
  • 2,818
  • 10
  • 29
  • Note that you can also create two mutable references to the same data in the same function, e.g. `let mut i = 0i32; let r1 = &mut i; let r2 = &mut *r1;`. – user4815162342 Dec 13 '21 at 17:45
  • @user4815162342, if you try to use them you WILL get an error: `fn main() { let mut i = 0; let r1 = &mut i; let r2 = &mut *r1; *r1 += 1; *r2 += 1; }` – hkBst Dec 14 '21 at 14:09
  • You won't get an error if you use `r2`, and only use `r1` after `r2` has been dropped. But that doesn't invalidate the question of why Rust allowed them to exist in the first place. I believe the reason is: 1) to allow re-borrowing, and 2) to allow projections, i.e. obtaining mutable reference to part of the data you refer to. Nested references are then a special case of projection. – user4815162342 Dec 14 '21 at 15:07