Search code examples
ruststructmutexautomatic-ref-countingownership

Borrowing issues using variables wrapped in Arc<Mutex<>> in Rust


  1. The code below can work.
#[derive(Debug)]
struct Test{
    str1 : Option<String>,
    str2 : Option<String>,
}
fn main() {
    let mut test = Test{
        str1 : Some(String::from("str1")),
        str2 : Some(String::from("str2"))
    };
    let str1 = test.str1.as_mut().unwrap();
    let str2 = test.str2.as_mut().unwrap();
    *str1 = String::from("nonono1");
    *str2 = String::from("nonono2");
    println!("{:?}", test);
}
  1. The code with Arc<Mutex<>> like this can work.
use std::sync::{
    Arc, Mutex
};
#[derive(Debug)]
struct Test{
    str1 : Option<String>,
    str2 : Option<String>,
}
fn main() {
    let test = Test{
        str1 : Some(String::from("str1")),
        str2 : Some(String::from("str2"))
    };
    let test = Arc::new(Mutex::new(test));
    let mut test = test.lock().unwrap();
    let str1 = test.str1.as_mut().unwrap();
    *str1 = String::from("nonono1");
    let str2 = test.str2.as_mut().unwrap();
    *str2 = String::from("nonono2");
    println!("{:?}", test);
}
  1. But the other code cannot work.
use std::sync::{
    Arc, Mutex
};
#[derive(Debug)]
struct Test{
    str1 : Option<String>,
    str2 : Option<String>,
}
fn main() {
    let test = Test{
        str1 : Some(String::from("str1")),
        str2 : Some(String::from("str2"))
    };
    let test = Arc::new(Mutex::new(test));
    let mut test = test.lock().unwrap();
    let str1 = test.str1.as_mut().unwrap();
    let str2 = test.str2.as_mut().unwrap();
    *str1 = String::from("nonono1");
    *str2 = String::from("nonono2");
    println!("{:?}", test);
}
  1. It seems that I cannot borrow two deffierent part of a Struct wrapped in Arc<Mutex<>>.And I can do this in the first code.

I want to mutably borrow two parts of a structure wrapped with Arc<Mutex<>> at the same time. Is this possible?


Solution

  • The issue is that when you call lock() it doesn't actually return &mut Test, it returns MutexGuard<Test>. Then when you access a field, it is accessed through the impl DerefMut on MutexGuard. Dereferencing through DerefMut causes the whole MutexGuard to be mutably borrowed, instead of just the field when you otherwise access it directly.

    You can resolve the issue, by upfront dereferencing MutexGuard into &mut Test by doing:

    let mut test = test.lock().unwrap();
    let test: &mut Test = &mut test;
    

    Which when putting it all together, looks like this (playground):

    use std::sync::{Arc, Mutex};
    
    #[derive(Debug)]
    struct Test{
        str1 : Option<String>,
        str2 : Option<String>,
    }
    
    fn main() {
        let test = Test{
            str1 : Some(String::from("str1")),
            str2 : Some(String::from("str2"))
        };
        let test = Arc::new(Mutex::new(test));
        
        let mut test = test.lock().unwrap();
        let test: &mut Test = &mut test;
        
        let str1 = test.str1.as_mut().unwrap();
        let str2 = test.str2.as_mut().unwrap();
        *str1 = String::from("nonono1");
        *str2 = String::from("nonono2");
        println!("{:?}", test);
    }