I just got started with rust before and wanted to try to write a Tetris. I ran into an error from the compiler when initializing before writing the game loop:
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`.
I saw the E0502, it is an error about the same variable first declaring an immutable reference and then calling its mutable reference function. Compilation passes when I swap 33 and 34.
But what I want to know is whether locking the mutex field of self is an immutable reference to self. I think this reference should be the mutex field.
Can’t I write code like this: now_block.next(self.pick_next_block());?
Here is a minimal, reproducible example:
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
}
}
Here is the code:
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);
}
}
>Solution :
fn pick_next_block(&mut self) -> u8
This is a function that mutably borrows self. It requires that mutable borrow to be held for the duration of the function call and no longer (as indicated by the fact that there are no lifetimes in the return type).
self.now_block.lock().unwrap();
Mutex::lock requires an immutable borrow of its Mutex. That means that this borrows self.now_block immutably. The return type of this function does contain the self lifetime, so the immutable borrow of the mutex must last as long as the variable containing the lock. That is, the signature of Mutex::lock is
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>>
or, written without the lifetime elision,
pub fn lock<'a>(&'a self) -> LockResult<MutexGuard<'a, T>>
So the lifetime 'a of self exists in the return type, which means we have to retain that borrow until the return value goes out of scope.
let x = self.pick_next_block();
let mut now_block = self.now_block.lock().unwrap();
In this order, x holds no resources. It’s a simple u8. So we borrow self mutably for pick_next_block, then we drop that borrow, and then we borrow self (technically self.now_block, but partial borrowing doesn’t come into play here) immutably for the duration of the now_block variable’s scope.
let mut now_block = self.now_block.lock().unwrap();
let x = self.pick_next_block();
In this order, we first borrow self immutably for the duration of now_block. Then we try to borrow self mutably, but we’re still retaining an immutable borrow, so the compiler complains.