You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:33:16