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
相关产品推荐
相关产品推荐

