Reputation: 125
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
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
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