conikeec
conikeec

Reputation: 209

Mutable pass-by-mutable-reference in Rust

I was working through this exercise (mutable pass-by-mutable reference)and not able to comprehend the result

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=42ff75b2464a99337b7738fba3597512

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

fn birthday_mutable<'a>(mut person: &'a mut Person, replacement: &'a mut Person) {
    println!("[InFn] Before : Alice {:p} : {:?}, Bob {:p} : {:?}", &person, person, &replacement, replacement);
    person = replacement;
    println!("[InFn] After  : Alice {:p} : {:?}", &person, person);
}

fn main() {
    let mut alice = Person {
        name: String::from("Alice"),
        age: 30,
    };
    let mut bob = Person {
        name: String::from("Bob"),
        age: 20,
    };
   
    println!("[Main] Before : Alice {:p} : {:?}, Bob {:p} : {:?}", &alice, alice, &bob, bob);
    birthday_mutable(&mut alice, &mut bob);
    println!("[Main] After :  Alice {:p} : {:?}, Bob {:p} : {:?}", &alice, alice, &bob, bob);
}

In the birthday_mutable function, person is in a mutable variable. The first thing we do is person = replacement;. This changes what our person variable is pointing at, and does not modify the original value being pointed at by the reference at all.

Despite for changing what person points to, yet the result is this

[Main] Before : Alice 0x7ffd0c9b77e0 : Person { name: "Alice", age: 30 }, Bob 0x7ffd0c9b7820 : Person { name: "Bob", age: 20 }
[InFn] Before : Alice 0x7ffd0c9b7568 : Person { name: "Alice", age: 30 }, Bob 0x7ffd0c9b7570 : Person { name: "Bob", age: 20 }
[InFn] After  : Alice 0x7ffd0c9b7568 : Person { name: "Bob", age: 20 }
[Main] After :  Alice 0x7ffd0c9b77e0 : Person { name: "Alice", age: 30 }, Bob 0x7ffd0c9b7820 : Person { name: "Bob", age: 20 }

Based on my understanding shouldn't the result be this?

[Main] Before : Alice 0x7ffd0c9b77e0 : Person { name: "Alice", age: 30 }, Bob 0x7ffd0c9b7820 : Person { name: "Bob", age: 20 }
[InFn] Before : Alice 0x7ffd0c9b7568 : Person { name: "Alice", age: 30 }, Bob 0x7ffd0c9b7570 : Person { name: "Bob", age: 20 }
[InFn] After  : Alice 0x7ffd0c9b7568 : Person { name: "Bob", age: 20 }
[Main] After :  Alice 0x7ffd0c9b77e0 : Person { name: "Bob", age: 20 } , Bob 0x7ffd0c9b7820 : Person { name: "Bob", age: 20 }

Can someone please explain why?

Upvotes: 0

Views: 1154

Answers (1)

eggyal
eggyal

Reputation: 126035

A simplified picture of the state of the stack when you entered birthday_mutable would be:

---- birthday_mutable ---
0x568 person       = 0x7e0
0x570 replacement  = 0x820

---- main ----
0x7e0 alice        = Person { name: "Alice", age: 30 }
0x820 bob          = Person { name: "Bob",   age: 20 }

Let's name the following mutables A and B:

fn birthday_mutable<'a>(mut person: &'a mut Person, replacement: &'a mut Person) {
                        ^^^A            ^^^B
  • Mutating A updates only what's stored at address 0x568, and thus only affects what reference birthday_mutable's person parameter holds—since parameters are local to their function (i.e. address 0x568 is in birthday_mutable's stack frame), this mutation does not affect the caller. It is what you are doing here:

    person = replacement;
    

    After executing the above statement, the memory at address 0x568 contains the value 0x820 (and thus birthday_mutable's person parameter now refers to bob)—you can see this by inspecting &*person instead of &person, i.e. the address of the thing to which person refers, rather than the address at which person itself is stored.


  • Mutating B updates only what's stored at whatever address (0x7e0) is itself held at address 0x568—that is, it mutates the data to which the person parameter refers (i.e. alice in main's stack frame). It is achieved by dereferencing the person parameter when mutating:

    *person = Person { name: "Charlie".to_owned(), age: 10 };
    

    Rust makes it easier to ignore dereferencing than some other systems languages because it automatically performs deref coercion in function calls and field accesses; nevertheless it's good to keep in mind what's going on under the hood.

Upvotes: 6

Related Questions