Trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<_>`

So, I had this question: how-to-run-a-member-function-in-a-thread-from-another-member-function, and I got great help with this.
I continued working and got to a new problem: playground.
I need to change a mutable struct member when the function receives self as Arc<Self>.
I get:

Data inside an Arc is immutable and cannot be modified.

There are two options:

  1. Use a mutex to get mutable access.
  2. Use the actor pattern.

It sounds like the actor pattern is the correct choice in your case. You can read about it here:

The article uses async/await, but you don't have to do that. The exact same ideas work with ordinary threads.

1 Like

Thanks Alice,
I just need to change one member and this solution is threads communication.
Isn't it a bit overkill?

Depends on your goals. If you create three treads and ends up with 2 or even 1 as threads_count then it maybe overkill, yes.

If you need something working then you need Mutex or atomics.

P.S. And yes, that's true not only on ARM (e.g.: Mabooks and mobiles), but x86 (PC), too.

1 Like

If you have a variable that is read and written by multiple threads, there has to be some form of inter-thread communication -- more specially thread synchronization.

Using an atomic is the simplest solution to use for incrementing a shared integer variable, but note that it still involves synchronization between processors that is performed by the hardware. It is good to to keep this in mind because it can impact performance.

1 Like

Thank you all for your replays.
I found Mutex to be the most easy and simple solution for my case.
playground
Would love to get a response if my solution or implementation is not the best.

There is no reason to clone the Arc to access the thread_count field. So this:

        let worker = self.clone();
        {
            let mut co = worker.thread_count.lock().unwrap();
            *co += 1;
        }

Can be simplified to this:

        {
            let mut co = self.thread_count.lock().unwrap();
            *co += 1;
        }

Also, since you have a Vec of threads that is protected by a mutex, you could use the Vec::len() and get rid of the thread_count. It may not matter in your application, but by having a redundant thread_count that is accessed by a separate mutex, there will be a window of time where the thread_count will be inaccurate since it is updated separately. It is usually best to avoid redundancy, and this is especially true for data shared between threads.

3 Likes

Also, what I forgot to do in the code I posted on your previous topic, you can pass self: &Arc<Self> to runner to avoid runner consuming self. This way you don't need to clone your Arc<Foo> in your main function in order to start the workers: playground.

2 Likes
    pub fn get_threads(&self) {
        let co = self.thread_count.lock().unwrap();
        println!("get_threads {}", *co);
    }

The above code you posted is simplified in the version posted by @jofas. But I wanted to point out that it is holding the mutex while doing the println! since co is a guard that is not dereferenced until calling println! and is not dropped until the end of the function. Whenever a mutex is used, it is best to hold it for as short a time as possible to reduce contention and blocking between threads.

The minimum work needed while holding the mutex is to simply dereference it to get the len:

    pub fn get_threads(&self) {
        let co = *self.thread_count.lock().unwrap();
        println!("get_threads {}", co);
    }

For details see the section "To unlock a mutex guard sooner than the end of the enclosing scope, either create an inner scope or drop the guard manually." under the Mutex Examples.

The code above is locking the mutex and getting the guard, and then dereferencing and dropping the guard, all in a single expression. That is also what the code does that @jofas posted.

It may be easier to understand if the guard is dropped explicitly or dropped when it goes out of scope at the end of a block, like this:

    pub fn get_threads(&self) {
        let co = {
            let guard = self.thread_count.lock().unwrap();
            *guard
        };
        println!("get_threads {}", co);
    }
1 Like

Thank you for the feedback,
I will remove the redundant clone.
The thread_count is there just for the experiment, I'm not using it In my production code.

That's actually helps a lot.
Thanks!

Good
I didn't know that Rust allows you to dereference a Mutex value outside the scope.

I'm not sure what you mean by that but it sounds like you have some fundamental misunderstanding. A mutex is not "dereferenceable". There is no access to any value outside its scope – Rust statically guarantees that you are only allowed to access a value as long as it lives, and that there are no dangling references to destroyed places.

@lioriz Did you mean you can dereference the Mutex guard inside a nested scope, like in my example code and the doc I linked?

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.