Dereferencing a MutexGuard

I'm working with a bit of complex (to me, atleast) code which I can't figure out. I've distilled my problem in the code below. In my actual code the reason I use Arc and Mutex is because I'm working with threads. The reason for the GuestState enum is that I would like to add different types of guests in the future without changing implementation of existing guests.

use std::sync::{Arc, Mutex};

enum GuestState {
    VIP(VIPState)
}

struct VIPState {
    name: String,
    something: bool,
}

fn main() {
    // This happens when program first starts
    let mut guest_state_mutexes: Vec<Arc::<Mutex<GuestState>>> = vec![];
    let state_mutex = Arc::new(Mutex::new(GuestState::VIP(VIPState {
        name: "Abc".to_string(),
        something: true,
    })));
    guest_state_mutexes.push(state_mutex);
    
    // This happens later in a different thread which has
    // to modify information inside a VIPState
    let mutex_for_use = guest_state_mutexes.get(0).unwrap();
    let state = mutex_for_use.lock().unwrap();
    
    match *state {      // PROBLEM
        GuestState::VIP(mut vip) => {
            vip.something = false;
        },
    }
}

If I don't deference the PROBLEM-row, I get an error saying types are mismatched, GuestState does not match MutexGuard<'_, GuestState>. If I explicitly deference state then I get an error saying VIPState does not implement Copy and implementing Copy is not possible because String is involved.

Is there an easy solution to this or am I approching this in a wrong way? I hope there is enough information to make educated guesses.

Rust Playground

This might work?

match &mut *state {      // PROBLEM
        GuestState::VIP(mut vip) => {
            vip.something = false;
        },
    }
}
use std::sync::{Arc, Mutex};

enum GuestState {
    VIP(VIPState)
}

struct VIPState {
    name: String,
    something: bool,
}

fn main() {
    // This happens when program first starts
    let mut guest_state_mutexes: Vec<Arc::<Mutex<GuestState>>> = vec![];
    let state_mutex = Arc::new(Mutex::new(GuestState::VIP(VIPState {
        name: "Abc".to_string(),
        something: true,
    })));
    guest_state_mutexes.push(state_mutex);
    
    // This happens later in a different thread which has
    // to modify information inside a VIPState
    let mutex_for_use = guest_state_mutexes.get(0).unwrap();
    let mut state = mutex_for_use.lock().unwrap();
    
    match &mut *state {      // PROBLEM
        GuestState::VIP(vip) => {
            vip.something = false;
        },
    }
}

Don't copy the state. Get it by reference and make state mutable so you can update the property.

1 Like

Aha! Indeed it leads me to the right path. I do need to make a few more mut declarations but the code then compiles. I need to test this on my actual code too.

Rust Playground

The ref mut is not necessary.

Or alternatively the &mut isn't needed.

Edition 2024 may disallow the use of both. (Or may not, I haven't kept up. It currently denies it on nightly edition 2024 with wishy-washy wording.)


The rest of the comment is a little exploration that may be helpful or may just be confusing :upside_down_face:.

Works on both editions without changing "binding modes":

#![deny(clippy::pattern_type_mismatch)]

    //    vvvvvv a place expression of type GuestState
    match *state { //   vvvvvvvvvvv taking a &mut to something in that place
        GuestState::VIP(ref mut vip) => { // (without moving anything)
            vip.something = false;
        },
    }

Works on both editions by using a ref mut binding mode:[1]

    //    vvvvvvvvvvv type `&mut GuestState`, results in ref mut binding mode
    match &mut *state { // due to the patterns being `GuestState`s
        //              vvv binds the same as the example above
        GuestState::VIP(vip) => {
            vip.something = false;
        },
    }

Works today but fails on edition 2024 currently:

    //    vvvvvvvvvvv type `&mut GuestState`, results in ref mut binding mode
    match &mut *state { // due to the patterns being `GuestState`s
        //              vvvvvvv attempt to "reset" the binding mode which is
        GuestState::VIP(ref mut vip) => { // effectively a no-op today (in this
            vip.something = false; // particular code), but may be denied
        },                         // in edition 2024
    }

The compiler suggested fix:[2]

#![deny(clippy::pattern_type_mismatch)]

    //    vvvvvvvvvvv type `&mut GuestState`, does not change binding mode since
    match &mut *state { // the patterns are also `&mut GuestState`s
        //                   vvvvvvvvvvv taking a &mut to something behind
        &mut GuestState::VIP(ref mut vip) => { // the `&mut GuestState`
            vip.something = false;
        },
    }

It's effectively the same as the first one, with &mut tacked on the front unnecessarily.


  1. does not pass the clippy lint used above ↩︎

  2. which passes the clippy lint again ↩︎

2 Likes

Oddly enough, it is. Not sure I'm doing it right but my actual app won't compile without the ref mut.