如何解决Rust中多次可变借用*self引发的编译错误?
解决Rust多次可变借用错误
问题背景
编写卡牌游戏练习时触发Rust多次可变借用错误,代码及错误信息如下:
原始代码
struct Card { val: i32, } struct Game { card1: Card, card2: Card, idx: usize, } impl Game { fn draw(&mut self) -> &mut Card { match self.idx { 0 => { self.idx = 1; &mut self.card1 } 1 => { self.idx = 0; &mut self.card2 } _ => &mut self.card1, } } fn do_something(&mut self, card1: &mut Card, card2: &mut Card) { card1.val += 1; card2.val += 2; } fn run(&mut self) { let card1 = self.draw(); let card2 = self.draw(); self.do_something(card1, card2); } } fn main() { let mut game = Game { card1: Card { val: 1 }, card2: Card { val: 2 }, idx: 0, }; game.run(); }
编译错误
error[E0499]: cannot borrow `*self` as mutable more than once at a time --> src/main.rs:31:21 | 30 | let card1 = self.draw(); | ---- first mutable borrow occurs here 31 | let card2 = self.draw(); | ^^^^ second mutable borrow occurs here 32 | self.do_something(card1, card2); | ----- first borrow later used here
错误原因
Rust借用检查器禁止同一时间存在多个对同一对象的可变借用。第一次调用draw时,self被可变借用并返回了&mut Card;第二次调用draw尝试再次可变借用self,且后续do_something仍在使用之前的可变引用,导致借用冲突。
修复方案
核心思路是确保同时存在的可变引用指向Game的不同独立字段(card1和card2),避免多次借用整个self。以下是两种符合要求的修复方式:
方式一:新增方法一次性获取两张卡牌的可变引用
这种方式简洁高效,直接满足do_something对两张卡牌的需求:
struct Card { val: i32, } struct Game { card1: Card, card2: Card, idx: usize, } impl Game { fn draw(&mut self) -> &mut Card { match self.idx { 0 => { self.idx = 1; &mut self.card1 } 1 => { self.idx = 0; &mut self.card2 } _ => &mut self.card1, } } fn do_something(&mut self, card1: &mut Card, card2: &mut Card) { card1.val += 1; card2.val += 2; } // 新增方法:返回两张卡牌的独立可变引用 fn get_both_cards(&mut self) -> (&mut Card, &mut Card) { (&mut self.card1, &mut self.card2) } fn run(&mut self) { // 一次性获取两张卡牌,避免多次借用self let (card1, card2) = self.get_both_cards(); self.do_something(card1, card2); // 若需维持draw的交替索引逻辑,可在此更新idx // self.idx = 1 - self.idx; } } fn main() { let mut game = Game { card1: Card { val: 1 }, card2: Card { val: 2 }, idx: 0, }; game.run(); }
方式二:调整run方法的借用逻辑
若需保留两次调用draw的形式,可通过解构self的可变引用,让借用检查器明确两个引用指向不同字段:
// 仅修改run方法,其余代码不变 fn run(&mut self) { let (card1, card2); let Game { card1: c1, card2: c2, idx } = self; // 第一次抽取 card1 = match *idx { 0 => { *idx = 1; c1 } 1 => { *idx = 0; c2 } _ => c1, }; // 第二次抽取 card2 = match *idx { 0 => { *idx = 1; c1 } 1 => { *idx = 0; c2 } _ => c1, }; self.do_something(card1, card2); }
关键说明
Rust允许同时存在多个指向不同字段的可变引用,只要这些引用不重叠。上述修复都确保了card1和card2的可变引用指向独立内存区域,通过了借用检查器的验证,同时保留了原方法的功能,且未复制卡牌。
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

