Daniil Iaitskov
Daniil Iaitskov

Reputation: 6039

Cannot send a struct over channel: mpsc::Sender cannot be shared between threads safely

I am struggling with sending a custom struct over channel.

I wrapped the value in an Arc and Mutex as described in the tutorial, but it doesn't compile anyway.

extern crate num;

use num::bigint::BigInt;
use std::io::{self, Write};
use std::sync::mpsc;
use std::thread;
use readline::readline as ask;
use std::sync::{Arc, Mutex};

enum Command {
    Quit,
    Help,
    Factorial { n: BigInt },
    Error { msg: String },
}

fn main() {
    let (input_tx, input_rx) = mpsc::channel();
    let input_thread = thread::spawn(|| {
        input_tx.send(Arc::new(Mutex::new(Command::Quit)));
    });
}
error: the trait bound `std::sync::mpsc::Sender<std::sync::Arc<std::sync::Mutex<Command>>>: std::marker::Sync` is not satisfied [E0277]
    let input_thread = thread::spawn(|| {
                       ^~~~~~~~~~~~~
help: run `rustc --explain E0277` to see a detailed explanation
note: `std::sync::mpsc::Sender<std::sync::Arc<std::sync::Mutex<Command>>>` cannot be shared between threads safely
note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<std::sync::Arc<std::sync::Mutex<Command>>>`
note: required because it appears within the type `[closure@src/main.rs:25:38: 65:6 input_tx:&std::sync::mpsc::Sender<std::sync::Arc<std::sync::Mutex<Command>>>]`
note: required by `std::thread::spawn`
error: aborting due to previous error

I am using Rust 1.10.0 (cfcb716cf 2016-07-03).

Upvotes: 0

Views: 2477

Answers (2)

Shepmaster
Shepmaster

Reputation: 430663

Look at this MCVE:

use std::thread;
use std::sync::mpsc;

enum Command {
    Quit,
    Error { msg: String },
}

fn main() {
    let (input_tx, input_rx) = mpsc::channel();
    let input_thread = thread::spawn(|| {
        input_tx.send(Command::Quit);
    });
}

And the error message:

`std::sync::mpsc::Sender` cannot be shared between threads safely; required because of the requirements on the impl of `std::marker::Send` for ` & std::sync::mpsc::Sender`

(emphasis mine)

By default, a closure captures a reference to any variables used within it. This is what you want most of the time, as giving up ownership is more restrictive for the creator of the closure. Using a reference allows the captured value to be shared between outside the closure and inside and doesn't require moving any bits around.

In this case, you do want to give ownership of input_tx to the closure. This is because ownership of the closure itself will be given to a new thread, so the closure and everything in it needs to be safe to be handed to another thread. A reference to a Sender may not be shared among threads.

A move closure requests that ownership of any captured variables be transferred to the closure. By doing this, there is no sharing and all the requirements are met. As aSpex said:

let input_thread = thread::spawn(move || {
    input_tx.send(Command::Quit);
});

Occasionally you need to transfer ownership of some captured variables but want to share others. Since move closures are all-or-nothing, you need to be more explicit in this case. You can simply take a reference before the closure:

let a = 42;
let a_ref = &a;

let input_thread = thread::spawn(move || {
    println!("{}", a_ref);
});

This doesn't work because the reference to a is not 'static, but shows the general idea.

Upvotes: 2

aSpex
aSpex

Reputation: 5216

The closure passed to thread::spawn() must be move (FnOnce). Arc and Mutex are not needed:

let input_thread = thread::spawn(move || {
    input_tx.send(Command::Quit);
});

Upvotes: 3

Related Questions