Deferences Mutex Structure Cannot Be a Variable

I'm working on a project that will have a number of different threads that all need to access the same structure. One thread will update the contents of the structure based on data read from sensors, while the other threads read the contents (for publishing to various services at various rates), so I am using a mutex.

Currently I have just two threads, a producer that populates the structure, and a consumer that reads the contents of the structure.

The thread that reads the structure looks like this:

let guard = data_mutex.lock().unwrap();
let sensor_hub_data = *guard;
log::info!(
"ALS: {}, White: {}, Lux: {}",
    sensor_hub_data.veml.raw_als,
    sensor_hub_data.veml.raw_white,
    sensor_hub_data.veml.lux,
);

The thread that populates the structure does so by doing the following.

let mut locked_mutex = data_mutex.lock().unwrap();
// Copy over the most recently send data from the channel into the structure.
match received_data {
    SensorData::Bsec { data } => (*locked_mutex).bsec = data,
    SensorData::Veml { data } => (*locked_mutex).veml = data,
}

As expected, the reader thread prints out fluctuating sensor data, somewhat like so:

I (618775) environment_monitor_rust: ALS: 0, White: 0, Lux: 0.0
I (628775) environment_monitor_rust: ALS: 91, White: 163, Lux: 5.2416
I (632935) environment_monitor_rust: ALS: 90, White: 162, Lux: 5.184
I (635005) environment_monitor_rust: ALS: 91, White: 162, Lux: 5.2416

To try to make the match statement tidier, I moved the dereference to be part of the variable above it, like so:

let mut locked_mutex = *data_mutex.lock().unwrap();
// Copy over the most recently send data from the channel into the structure.
match received_data {
    SensorData::Bsec { data } => locked_mutex.bsec = data,
    SensorData::Veml { data } => locked_mutex.veml = data,
}

When I changed it to be like this, the thread that reads the structure no longer seems to be seeing updated data. It's output looks now looks like this:

I (618775) environment_monitor_rust: ALS: 0, White: 0, Lux: 0.0
I (628775) environment_monitor_rust: ALS: 0, White: 0, Lux: 0.0
I (632935) environment_monitor_rust: ALS: 0, White: 0, Lux: 0.0
I (635005) environment_monitor_rust: ALS: 0, White: 0, Lux: 0.0

Why does moving the de-referencing in the producer outside of the match expression cause the structure to no longer be updated?

You're creating a copy, like if I did this:

fn does_not_update_original(data: &mut i32) {
    let copy_of_data = *data;
    copy_of_data = 42;
}
// instead of
fn update_original(data: &mut i32) {
    *data = 42;
}

The former copies (or attempts to move) out of the place expression *data in order to assign the value to the new variable, and then updates that copy; the latter updates the place expression *data directly.

In your code, locked_mutex isn't a mutex guard anymore, it's a copy of the data within.

That said you can probably rewrite the original as...

match received_data {
    SensorData::Bsec { data } => locked_mutex.bsec = data,
    SensorData::Veml { data } => locked_mutex.veml = data,
}

due to deref coercion and the fact that field expressions are also place expressions.

1 Like

Huh. Interesting.
So if I was (for some bizarre reason) wanting to have a variable to the deferenced data, how would I go about doing that?

To a field of the struct within the Mutex? Like so.

fn example(mutex: Mutex<S>) {
    let mut guard = mutex.lock().unwrap();
    let _ref = &guard.field;
    let _mut_ref = &mut guard.field;
}

Cool. Thank you