RefCell alternative that can be `Send`?

Viewed 687

I was designing a type that can be sent between threads or not, depending if it's an Owned type or not.

This code:

use std::cell::RefCell;
use std::rc::Rc;

pub enum M<'a, T> {
    Owned(),
    Ref(Rc<RefCell<&'a mut [T]>>)
}

fn main() {
    let m: M<'static, u8> = M::Owned();
    std::thread::spawn(||{
        let m = m;
    });
}

gives

error[E0277]: `Rc<RefCell<&mut [_]>>` cannot be sent between threads safely
   --> src/main.rs:11:5
    |
11  |       std::thread::spawn(||{
    |  _____^^^^^^^^^^^^^^^^^^_-
    | |     |
    | |     `Rc<RefCell<&mut [_]>>` cannot be sent between threads safely
12  | |         let m = m;
13  | |     });
    | |_____- within this `[closure@src/main.rs:11:24: 13:6]`

I understand that this is not Send because RefCell is !Send. I think this is the only thing preventing M from being Send. Is there a RefCell alternative that is Send?

1 Answers

I understand that this is not Send because RefCell is !Send. I think this is the only thing preventing M from being Send.

Yes, this is correct.

Is there a RefCell alternative that is Send?

Of course, at least two. If you just want the &mut in another thread, you can actually just send it:

#[derive(Debug)]
pub enum M<'a, T> {
    Owned(),
    Ref(&'a mut [T])
}

fn main() {
    let m: M<'static, u8> = M::Owned();
    std::thread::spawn(move ||{
        println!("{:?}", m);
    }).join().unwrap();
}

But if you use the Rc<RefCell<_>> to e.g. make your &mut sharable, you can achieve the same thing in the context of multithreading with Arc<Mutex<_>>, as @Locke already mentioned:

use std::sync::{Arc, Mutex};

#[derive(Debug, Clone)]
pub enum M<'a, T> {
    Owned(),
    Ref(Arc<Mutex<&'a mut [T]>>)
}

fn main() {
    let m: M<'static, u8> = M::Owned();
    let m2 = m.clone();
    std::thread::spawn(move ||{
        println!("{:?}", m2);
    }).join().unwrap();
    println!("{:?}", m);
}
Related