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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.