如何在方法中获取对结构字段的可变引用并在没有借用检查器抱怨的情况下调用其他方法?

How to take mutable references to struct fields in a method and call other methods without the borrow checker complaining?

我有这个代码:

struct Player {}

impl Player {
    fn update(&mut self) {}
}

struct GameBoard {
    p1: Player,
    p2: Player,
}

impl GameBoard {
    fn update(&mut self) {}
    fn update_current_player(&mut self, p1_turn: bool) {
        if p1_turn {
            self.p1.update()
        } else {
            self.p2.update();
        }

        self.update();

        if p1_turn {
            self.p1.update()
        } else {
            self.p2.update();
        }
    }
}

我不想在每次更新当前播放器时都输入 if 语句。在我用过的其他语言中,我会这样写:

struct Player {}

impl Player {
    fn update(&mut self) {}
}

struct GameBoard {
    p1: Player,
    p2: Player,
}

impl GameBoard {
    fn update(&mut self) {}
    fn update_current_player(&mut self, p1_turn: bool) {
        let p = if p1_turn { &mut self.p1 } else { &mut self.p2 };
        p.update();

        self.update();

        p.update();
    }
}

但这惹恼了借阅检查员:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/lib.rs:18:9
   |
15 |         let p = if p1_turn { &mut self.p1 } else { &mut self.p2 };
   |                                                    ------------ first mutable borrow occurs here
...
18 |         self.update();
   |         ^^^^ second mutable borrow occurs here
19 | 
20 |         p.update();
   |         - first borrow later used here

一定有某种方法可以不必每次都输入 if 语句。我也不希望将 if 语句分解为一个采用 p1_turn 的函数,因为写出函数调用仍然相当长。有人有什么建议吗?

我理解为什么如果您正在访问 Enum 或堆上的数据,这种模式是不安全的,因为对 self.update() 的调用可能会使引用无效,但在这种情况下,引用self.update() 中的任何代码都不可能使它无效,因为它只是引用结构的一个字段。

There must be some way not to have to type out the if statement every time. I would also prefer not to break the if statement out into a function that takes p1_turn, because writing out the function call is still fairly long.

鉴于这些要求,您可以创建一个匿名闭包来捕获 p1_turn 变量并重新使用它。但是,为了避免重叠可变借用,您必须将 &mut self 作为参数传递给闭包。示例:

struct Player {}

impl Player {
    fn update(&mut self) {}
}

struct GameBoard {
    p1: Player,
    p2: Player,
}

impl GameBoard {
    fn update(&mut self) {}
    fn update_current_player(&mut self, p1_turn: bool) {
        let p_update = |gb: &mut GameBoard| {
            if p1_turn {
                gb.p1.update();
            } else {
                gb.p2.update();
            }
        };

        p_update(self);
        self.update();
        p_update(self);
    }
}

playground