kek_mek
kek_mek

Reputation: 125

Rust: How to use methods defined in traits within newly spawned threads using Arc?

So I am trying to define a method in a trait that would spawn a thread and make use of another trait method, but I am a bit stuck on how to "unpack" it from Arc<...>:

use std::sync::Arc;
use std::sync::Mutex;
use websocket::{Message, WebSocketResult};


trait Sender<S>
where
    S: Into<Message<'static>> + Send,
{
    fn send_once(&mut self, message: S) -> WebSocketResult<()>;
    fn send_in_thread(&mut self, sleep_interval: time::Duration) -> WebSocketResult<()> {
        let self_copy = Arc::new(Mutex::new(self)).clone();
        let thread_join_handle = thread::spawn(move || self_copy.send_once(message));
        thread_join_handle.join().unwrap()
    }
}

The error I get is:

no method named `send_once` found for struct `std::sync::Arc<std::sync::Mutex<&mut Self>>` in the current scope

method not found in `std::sync::Arc<std::sync::Mutex<&mut Self>>`

Which is fair, I didn't define such a method on this wrapper type, but how do I get out of this situation the shortest way? Or, the most idiomatic way? I used Arc because previously I had Self cannot be sent between threads safely if I didn't use it.

Upvotes: 0

Views: 739

Answers (2)

cameron1024
cameron1024

Reputation: 10136

There's a couple of things going on here:

Arc<T> is used to provide "shared ownership". By default, a value has a single owner, which ensures that each value is dropped exactly once. If the same piece of data could be owned by 2 variables, it would be dropped twice. An Arc bypasses this restriction by providing a different Drop implementation: "if there are other references, decrease the reference count by one, otherwise, drop the wrapped data".

Arc dereferences to T via the Deref trait. This means that something like the following will work:

let string = Arc::new("hello");
println!("{}", string.len());

Note there is no "unpacking" needed, this happens implicitly, and is explained in some detail in this question: What is the relation between auto-dereferencing and deref coercion?

Mutex does a different job. It allows an otherwise non-thread-safe value to be shared safely between threads, by performing "locking" to prevent simultaneous reads/writes.

Because of this, if you have a Mutex<i32>, you can't just treat that as an i32, you first have to acquire the lock, by calling .lock(), and then handle the Result you get back in case the mutex was poisoned.

TLDR: use self_copy.lock().unwrap().send_once(), or .lock() and handle the error case

Upvotes: 1

Aplet123
Aplet123

Reputation: 35512

You need to lock the mutex to obtain a MutexGuard before you can call methods on it:

let thread_join_handle = thread::spawn(move || self_copy
    .lock()
    .unwrap()
    .send_once(message));

Upvotes: 1

Related Questions