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

Rust中锁与非锁函数顺序交换为何触发E0502错误?

关于Rust中Mutex借用与E0502错误的疑问

我刚入门Rust,尝试编写俄罗斯方块时,在初始化阶段(游戏循环前)遇到编译器错误:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/game.rs:34:12
   |
33 |             let mut now_block = self.now_block.lock().unwrap();
   |                                 --------------------- immutable borrow occurs here
34 |             let x = self.pick_next_block();
   |                     ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
35 |             now_block.next(x);
   |             ----------------- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.

我知道E0502是同一变量先被不可变借用后又可变借用的错误,交换第33行和34行代码后编译通过。但我有两个疑问:

  • 锁定self的Mutex字段是否属于对self的不可变借用?我认为应该仅借用该Mutex字段。
  • 能否编写now_block.next(self.pick_next_block());这类代码?

最小可复现示例

use std::sync::Mutex;

struct MyStruct {
    m: Mutex<i32>
}
impl MyStruct {
    fn fn1(&mut self) {
        let mut m = self.m.lock().unwrap();
        *m += self.fn2();
    }
    fn fn2(&mut self) -> i32{
        2
    }
}

完整代码

use crate::utils::{Block, Node};
use std::collections::VecDeque;
use std::sync::Mutex;
use rand::random;

pub struct Game {
    map: Mutex<[[Node; 10]; 20]>,
    now_block: Mutex<Block>,
    packs: Mutex<VecDeque<u8>>,
    score: Mutex<i32>,
}

impl Game {
    pub fn init() -> Self {
        let score = Mutex::new(0 as i32);
        let map = Mutex::new(
                    [[Node::init([' ', '.'], (255, 255, 255), 0); 10]; 20]);
        let now_block = Mutex::new(
                    Block::init(1, (1, 4), 4));
        let packs = Mutex::new(
                    VecDeque::<u8>::new());
        Self {
            map,
            score,
            now_block,
            packs,
        }
    }

    pub fn run(&mut self) {
        self.create_packs();
        {
            let mut now_block = self.now_block.lock().unwrap();
            let x = self.pick_next_block();
            now_block.next(x);
        }
        let packs = self.packs.lock().unwrap();
        println!("{:?}", packs);
    }

    fn create_packs(&mut self) {
        let mut packs = self.packs.lock().unwrap();
        let mut tmp_vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7];
        for i in (1..=7).rev() {
            let x = random::<usize>() % i;
            packs.push_back(tmp_vec[x]);
            (tmp_vec[i - 1], tmp_vec[x]) = (tmp_vec[x], tmp_vec[i - 1]);
        }
    }

    fn pick_next_block(&mut self) -> u8 {
        let flag: bool;
        let out: u8;
        {
            let mut packs = self.packs.lock().unwrap();
            out = packs.pop_front().unwrap();
            flag = packs.len() < 7;
        }
        if flag {
            self.create_packs();
        }
        out
    }
}
// block
pub struct Block {
    kind: u8,
    pos: (u8, u8),
    now_shape: u8,
}

impl Block {
    pub fn init(kind: u8, pos: (u8, u8), now_shape: u8) -> Self {
        Self {
            kind,
            pos,
            now_shape,
        }
    }
    pub fn next(&mut self, kind: u8) {
        self.kind = kind;
        self.now_shape = 0;
        // TODO
        self.pos = (0, 0);
    }
}

解答

1. 锁定Mutex字段是否属于对self的不可变借用?

是的,这里确实产生了对self的不可变借用。原因在于:

  • 访问self.now_block时,需要先不可变借用self(仅读取字段未修改),再调用Mutex::lock方法——该方法接收&Mutex<T>类型参数,也就是不可变引用。
  • lock返回的锁守卫(MutexGuard<T>)会持有对Mutex的引用,这意味着self的不可变借用生命周期会延续到锁守卫存在的整个期间(直到now_block变量离开作用域被销毁)。
  • 此时调用self.pick_next_block()需要对self进行可变借用,而Rust的借用规则不允许同一时间存在同一变量的不可变借用和可变借用,因此触发E0502错误。

你提到的“仅借用该Mutex字段”逻辑上是对的,但在Rust的借用检查中,借用字段本质是借用了self的一部分——只要self的任何部分被不可变借用,整个self就不能被可变借用,这是借用规则的核心约束之一。

2. 能否编写now_block.next(self.pick_next_block());这类代码?

不能,这样写依然会触发E0502错误。原因是:

  • 该表达式中now_block仍持有对self的不可变借用(锁守卫未销毁),而self.pick_next_block()需要对self的可变借用。
  • 不管函数参数的求值顺序如何,只要同一表达式中同时存在这两种借用,就会违反Rust的借用规则。
  • 正确做法还是先调用pick_next_block()获取结果(确保可变借用结束),再调用now_block.next(x);或者调整代码顺序,先完成可变借用操作,再获取锁。

内容的提问来源于stack exchange,提问作者paradoxD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:52:11