Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does swapping the order of a lock and a function not related to the lock in rust cause an error?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading