error[E0277]: cannot be shared between threads safely


#1

I have an issue with my code.

The main code

pub struct PermutationIteratorThread {

}

use std::thread;

#[derive(Debug, PartialEq)]
pub enum PermutationIteratorEvent<T>
{
    Data( usize, Vec<T> ),
    End,
}



use PermutationIteratorEvent::{Data, End};

impl PermutationIteratorThread
{
     pub fn new<T, F>( input : Vec<T>, f : &'static mut F )
        where 
            T: 'static + PartialOrd + Ord + Clone + Send,
            F: 'static + FnMut(PermutationIteratorEvent<T>) + Send
    {

        let iterator = PermutationIterator::new(input);

        let computer_handler = thread::spawn( move || {
            for (index, permutation) in iterator.enumerate() {
                f( Data( index, permutation ) );
            }
            f( End )
        });
        computer_handler.join();
     }
}

The integration test code

extern crate permutation_way;

use permutation_way::PermutationIteratorThread;
use permutation_way::PermutationIteratorEvent::*;

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

use std::time::Duration;

#[test]
fn should_be_coded_with_threads() {

    // counter of permutation received
    let mut counter = 0;

    let (tx, rx) = mpsc::channel();


    let input = vec![1, 2, 3];

    let receiver = | event | {
        println!( "evenement reçus {:?}", event );
        match event {
            Data( index, permutation) => {
                match index {
                    0 => assert_eq!( vec![1, 2, 3], permutation ),
                    1 => assert_eq!( vec![1, 3, 2], permutation ),
                    2 => assert_eq!( vec![3, 1, 2], permutation ),
                    3 => assert_eq!( vec![3, 2, 1], permutation ),
                    4 => assert_eq!( vec![2, 3, 1], permutation ),
                    5 => assert_eq!( vec![2, 1, 3], permutation ),
                    // 1 -> assert_eq!( None, iterator.next() ),
                    // 1 -> assert_eq!( false, iterator.has_errors() ),
                    _ => (),
                }
                counter = counter + 1
            },
            End => {
                assert_eq!( 6, counter );
                tx.send( event ).unwrap()
            }

        }
    };


    // call
    PermutationIteratorThread::new( input, & mut receiver );

    // assertions
    // wait receiving the end event
    let result = rx.recv_timeout( Duration::from_secs(10) );
    assert_eq!( End, result.unwrap() );
    
}

The error output

error[E0277]: `std::sync::mpsc::Sender<permutation_way::PermutationIteratorEvent<i32>>` cannot be shared between threads safely
  --> tests\threads_it.rs:49:5
   |
49 |     PermutationIteratorThread::new( input, & mut receiver );
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::sync::mpsc::Sender<permutation_way::PermutationIteratorEvent<i32>>` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<permutation_way::PermutationIteratorEvent<i32>>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<permutation_way::PermutationIteratorEvent<i32>>`
   = note: required because it appears within the type `[closure@tests\threads_it.rs:22:20: 45:6 counter:&mut i32, tx:&std::sync::mpsc::Sender<permutation_way::PermutationIteratorEvent<i32>>]`
   = note: required by `permutation_way::PermutationIteratorThread::new`

error: aborting due to previous error

I do not understand where is the issue.

Please advise.


#2

Rust’s MPSC channel is designed in such a way that the interface used for sending data into the channel, Sender, is thread-private. This allows an easier implementation and more performance optimizations under the hood. So you cannot share a Sender across multiple threads, all you can do is make one (cheap) clone of it per thread. In trait terms, a Sender is Send but not Sync.

Here, however, your “receiver” closure, which captures a reference to the channel input (“tx”), is sent to another thread. That could be a race condition at runtime if you kept using tx from the main thread, so Rust helpfully prevents it from compiling. In trait terms, a reference &T is only Send if the underlying type T is Sync, and Sender isn’t.

I think you can fix this by forcefully moving the channel input into the closure with the “move” keyword, which should make the closure Send. This will also help you with lifetime issues later on, in general “move” is what you want when preparing a closure for use by an independent thread (in lifetime terms, it helps you make your closure 'static).

You may also want PermutationIteratorThread’s constructor to take its input closure by value, rather than by reference, as otherwise I predict lifetime trouble. “receiver” , as a local variable of the main thread, does not have 'static lifetime.