0

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.

kek_mek
  • 105
  • 4

2 Answers2

1

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));
Aplet123
  • 33,825
  • 1
  • 29
  • 55
1

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

cameron1024
  • 9,083
  • 2
  • 16
  • 36