Reputation: 4666
I have a struct RabbitMQBackend
which implements a trait Backend
like:
pub trait Backend {
// Start consuming message from the queue.
fn pull(&self, sender: &Sender<String>);
}
pub struct RabbitMQBackend {
// Some fields ...
}
impl Backend for RabbitMQBackend {
fn pull(&self, sender: &Sender<String>) {do something...}
}
I am creating an instance of this struct like:
let rmq_backend = RabbitMQBackend::new("amqp://user:password@localhost:5672/", "testqueue2");
let mut consumer = ThreadConsumer::new();
consumer.consume(&rmq_backend);
where ThreadConsumer
is:
pub struct ThreadConsumer {
pub sender: Sender<String>,
pub receiver: Receiver<String>,
}
impl Consumer for ThreadConsumer {
fn new() -> Self {
let (sender, receiver) = bounded(3);
ThreadConsumer {
sender: sender,
receiver: receiver,
}
}
fn consume(&mut self, backend: &impl Backend) {
let consumer = thread::spawn(move || {
backend.pull(&self.sender);
});
}
}
The problem is I am trying to call the backend.pull
function from inside a separate thread, but I am getting this error:
error[E0277]: `impl Backend` cannot be shared between threads safely
--> src/consumer/thread_consumer/thread_consumer.rs:23:24
|
23 | let consumer = thread::spawn(move || {
| ^^^^^^^^^^^^^ `impl Backend` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `impl Backend`
help: consider further restricting this bound with `+ std::marker::Sync`
--> src/consumer/thread_consumer/thread_consumer.rs:20:37
|
20 | fn consume(&mut self, backend: &impl Backend) {
| ^^^^^^^^^^^^
= note: required because of the requirements on the impl of `std::marker::Send` for `&impl Backend`
= note: required because it appears within the type `[closure@src/consumer/thread_consumer/thread_consumer.rs:23:38: 25:10 backend:&impl Backend, self:&mut consumer::thread_consumer::thread_consumer::ThreadConsumer]
Note 1: I tried implementing the Send and Senc trait for the Backend trait and RabbitMQBackend struct like:
pub trait Backend: Send + Sync {
// Start consuming message from the queue.
fn pull(&self, sender: &Sender<String>);
}
pub struct RabbitMQBackend {
// Some fields ...
}
unsafe impl Send for RabbitMQBackend {}
unsafe impl Sync for RabbitMQBackend {}
then passed the backend function arg like
fn consume(&mut self, backend: &impl Backend + Send + Sync) {...}
but it raised the following error
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
How can I resolve this issue?
Upvotes: 3
Views: 2324
Reputation: 42302
The native Rust threads are not scoped. This means once the thread is spawned it will live its life independent from its creator (as far as the Rust compiler is concerned anyway). So if you want to move a reference into a thread, that reference needs to live forever (aka 'static
) as, as far as the compiler is concerned, the creator thread could die immediately and the child never.
So I see two solutions there:
don't do that, use something like Arc (and probably a mutex or rwlock depending on the thread safety of the backend) or somesuch for your backend, such that your backend has "multiple owners" via the Arc, this way you obviate the ref' issue
use scoped threads
Upvotes: 2