Does this mean that immutable references and mutable references exist at the same time?

When I wrote the Gobang program, I found that I could not modify the game state(&mut self) while passing the immutable reference of the game to the display instance and the algorithm instance. Because this will result in one mutable reference and multiple immutable references.
However, when I move the game instance into the display instance, and let the display instance be responsible for modifying the game state and giving the immutable reference of the game instance. I found that I can give immutable references while modifying the game state. Does this mean that there is one mutable reference and multiple immutable references at the same time? Further, is this implementation concurrency safe?
The following is the runnable Code:

struct Game {
    a: i32,
}

struct Display {
    game: Game,
}
impl Display {
    fn new(game: Game) -> Self {
        Display { game }
    }
    fn change_game(&mut self) {
        self.game.a += 1;
    }

    fn give_a_immutable_ref(&self) -> &Game {
        &self.game
    }
}

fn main() {
    let game = Game { a: 0 };
    let mut display = Display::new(game);

    // game loop
    for i in 0..4 {
        display.change_game();
        println!("{}", display.give_a_immutable_ref().a);
    }
}

The following is the code that failed to compile:

struct Game {
    a: i32,
}

impl Game {
    fn change_a(&mut self) {
        self.a += 1;
    }
}

struct Display<'a> {
    game: &'a Game,
}
impl<'a> Display<'a> {
    fn new(game: &'a Game) -> Self {
        Display { game }
    }

    fn give_a_immutable_ref(&self) -> &Game {
        &self.game
    }
}

fn main() {
    let mut game = Game { a: 0 };
    let display = Display::new(&game);

    for i in 0..4 {
        game.change_a();
        println!("{}", display.give_a_immutable_ref().a);
    }
}

Your code never has an immutable and mutable reference at the same time.

1 Like

Thank you very much for your reply!
But I'm still confused.

Does this mean that if I want to have a data structure similar to one mutable reference and multiple immutable references, I can use a data structure to wrap the mutable variable, and this data structure is responsible for changing the internal variable and distributing the immutable references. Thereby achieving the effect of having one modifier and a plurality of observers at the same time.

Thanks again!

We can modify the first example to attempt getting a mutable borrow and a shared borrow at the same time:

-    fn change_game(&mut self) {
+    fn change_game<'a>(&'a mut self) -> &'a() {
         self.game.a += 1;
+        &()
     }

-    for i in 0..4 {
-        display.change_game();
-        println!("{}", display.give_a_immutable_ref().a);
-    }
+    for _ in 0..4 {
+        let x: &() = display.change_game();
+        // `display` is borrowed as mutable until we drop `x`
+        println!("{}", display.give_a_immutable_ref().a); // can't borrow as immutable at the same time
+        drop(x);
+    }

(Playground)

This causes an error now:

error[E0502]: cannot borrow `display` as immutable because it is also borrowed as mutable
  --> src/main.rs:30:24

Try to move the println! line below the drop and it will work again. Interestingly, removing the drop(x); line will work too. I guess that's because the compiler is smart enough to know that the shared reference isn't used later and thus can be dropped early. We can prohibit that as follows:

use std::marker::PhantomData;

struct DropMonitor<'a>(PhantomData<&'a ()>);

impl<'a> Drop for DropMonitor<'a> {
    fn drop(&mut self) {
        println!("Dropped");
    }
}


/* … */

fn main() {
    let game = Game { a: 0 };
    let mut display = Display::new(game);

    // game loop
    for _ in 0..4 {
        let _monitor: DropMonitor = display.change_game();
        //drop(_monitor); // uncomment to end mutable borrow here
        println!("{}", display.give_a_immutable_ref().a);
    }
}

(Playground)

Here, as long as _monitor isn't dropped, there will be a mutable borrow, so we also get an error (unless we uncomment the drop(_monitor); line).


Now to your second example:

struct Display<'a> {
    game: &'a Game,
}

/* … */

fn main() {
    let mut game = Game { a: 0 };
    let display = Display::new(&game);

    for i in 0..4 {
        game.change_a();
        println!("{}", display.give_a_immutable_ref().a);
    }
}

(Playground)

Display has a lifetime 'a. The borrow done at &game will last until display is dropped.

You could fix it as follows (I think):

 struct Display<'a> {
-    game: &'a Game,
+    game: &'a mut Game,
 }
 impl<'a> Display<'a> {
-    fn new(game: &'a Game) -> Self {
+    fn new(game: &'a mut Game) -> Self {
         Display { game }
     }

     fn give_a_immutable_ref(&self) -> &Game {
         &self.game
     }
 }

 fn main() {
     let mut game = Game { a: 0 };
-    let display = Display::new(&game);
+    let display = Display::new(&mut game);

     for i in 0..4 {
-        game.change_a();
+        display.game.change_a();
         println!("{}", display.give_a_immutable_ref().a);
     }
 }

(Playground)

1 Like

The immutable reference returned by display.give_a_immutable_ref only lives for a very short period, to fetch .a. After the println! is done, that reference is no longer there. By the time you mutate the game in display.change_game() in the next iteration, there are no other references around.

1 Like

What an impressive answer! Thank you very much! This is really helpful to me!

Thank you very much! Really is a concise and explicit explanation.
Lifetime really makes me dizzy.. @_@

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.