0

I have having difficulty implementing Send for a trait. Full code in the playground.

I have a Storage trait :

pub trait Storage {
    fn get_value(&self, key: &str) -> Result<Vec<u8>, Error>;
    fn put_value(&mut self, key: &str, value: &[u8]) -> Result<(), Error>;
    fn key_exists(&self, key: &str) -> bool;
    fn delete_key(&mut self, key: &str) -> Result<(), Error>;
}

It is part of the Consistency struct:

pub struct Consistency<'a> {
    storage: &'a mut Storage,
}

I have implemented a MemoryStorage struct:

#[derive(Debug)]
struct MemoryStorage {
    data: HashMap<String, Vec<u8>>,
}

impl Storage for MemoryStorage {
    fn get_value(&self, key: &str) -> Result<Vec<u8>, Error> {
        self.data.get(key).map_or(
            Err(Error::new(ErrorKind::NotFound, "Key does not exists")),
            |v| Ok(v.clone()),
        )
    }

    fn put_value(&mut self, key: &str, value: &[u8]) -> Result<(), Error> {
        self.data.insert(key.to_string(), Vec::from(value));
        Ok(())
    }

    fn key_exists(&self, key: &str) -> bool {
        self.data.contains_key(key)
    }

    fn delete_key(&mut self, key: &str) -> Result<(), Error> {
        self.data.remove(key).map_or(
            Err(Error::new(ErrorKind::NotFound, "Key does not exists")),
            |v| Ok(()),
        )
    }
}

I wrote a test:

#[test]
fn multiple_threads_bombing_storage() {
    const parallels: usize = 10;
    let mut storage = MemoryStorage {
        data: HashMap::new(),
    };
    let mut consistency = Consistency::new(&mut storage);

    // create all keys.
    let mut keys = Vec::<String>::new();
    for k in 1..parallels {
        keys.push(k.to_string());
    }

    let zero = "0".as_bytes();
    // put default values in files.
    for key in keys.clone() {
        consistency.put_value(&key, zero).unwrap();
    }

    // test write threads
    let write_thread = |to_write: Vec<u8>, keys: &mut Vec<String>| {
        let mut rng = rand::thread_rng();
        rng.shuffle(keys);

        for key in keys {
            let mut val = consistency.get_value(key).unwrap();
            val.append(to_write.clone().as_mut());
            consistency.put_value(key, &val).unwrap();
        }
    };

    // start parallels threads..
    for t in 1..parallels {
        let handle = thread::spawn(move || {
            write_thread(t.to_string().into_bytes(), &mut keys);
        });
    }

    // wait for all threads..

    // check if all parallels keys have all numbers in any order.
}

This gives me an error:

error[E0277]: the trait bound `fileapi::Storage: std::marker::Send` is not satisfied
   --> src/consistency.rs:158:26
    |
158 |             let handle = thread::spawn(move || {
    |                          ^^^^^^^^^^^^^ `fileapi::Storage` cannot be sent between threads safely
    |
    = help: the trait `std::marker::Send` is not implemented for `fileapi::Storage`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&mut fileapi::Storage`
    = note: required because it appears within the type `consistency::Consistency<'_>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&mut consistency::Consistency<'_>`
    = note: required because it appears within the type `[closure@src/consistency.rs:145:28: 154:10 consistency:&mut consistency::Consistency<'_>]`
    = note: required because it appears within the type `[closure@src/consistency.rs:158:40: 160:14 write_thread:[closure@src/consistency.rs:145:28: 154:10 consistency:&mut consistency::Consistency<'_>], t:usize, keys:std::vec::Vec<std::string::String>]`
    = note: required by `std::thread::spawn`

Adding unsafe impl Send for Storage {} gives me a contradictory error that Send can't be implemented for a trait:

cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `fileapi::Storage + 'static`

I don't understand how to solve this problem.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ashish Negi
  • 5,193
  • 8
  • 51
  • 95
  • 1
    [One does not simply implement `Send`](https://doc.rust-lang.org/beta/nomicon/send-and-sync.html). Also, please build a better MCVE, so that we can reproduce the problem. Essential parts of the code seem to be missing. – E_net4 Nov 16 '17 at 20:17
  • 1
    @E_net4 full compiling example link is at the top of question. – Ashish Negi Nov 16 '17 at 20:22
  • 1
    Using `&'a mut (Storage + Send)` instead of `&'a mut Storage` will make `Consistency` implement `Send`, but there are so many other errors I don't consider this a real answer... – Stefan Nov 16 '17 at 21:17
  • I am using `Arc` and now i have two errors : https://play.rust-lang.org/?gist=afd3d6536e4dd6c7cf6ca4f8b1755a89&version=stable I am not able to get through them. – Ashish Negi Nov 16 '17 at 21:19

1 Answers1

5

You have a reference to something implementing a trait (a trait object), but your trait isn't guaranteed to be able to be sent between threads.

You need to ensure that it is. You can require all implementors of your trait are Send:

pub trait Storage: Send {
    // ...
}

Or that this specific trait object implements Send:

pub struct Consistency<'a> {
    storage: &'a mut (Storage + Send),
}

Your code has many other issues, so it still doesn't compile. Among them:

  1. You attempt to pass a stack-allocated variable to a thread.
  2. You try to share a mutable reference among multiple threads.

You many need to go back and re-read The Rust Programming Language, especially the chapter on concurrency.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • can you please take a look at : https://play.rust-lang.org/?gist=afd3d6536e4dd6c7cf6ca4f8b1755a89&version=stable ? It has 2 errors around closure and threads. – Ashish Negi Nov 16 '17 at 21:20
  • @AshishNegi sorry, no. Those issues are already covered by other questions here, some of which I've already linked to. Be thankful that Rust has caught all of these errors at compile time. C or C++ would probably let you compile this code and it would work fine, until it didn't. – Shepmaster Nov 16 '17 at 21:22
  • ok. thanks, i will read through the shared links. One question : If `storage` is mutable, as in `Consistency's field`, is it possible to share it across threads in rust ? – Ashish Negi Nov 16 '17 at 21:24
  • 2
    @AshishNegi no, it is not possible to share a `&mut` between threads. This would violate the [rules of references](https://doc.rust-lang.org/stable/book/second-edition/ch04-02-references-and-borrowing.html#the-rules-of-references), which state that there can only be one unique mutable reference at a time. That's why I've linked to a question that shows how to share a mutable object between threads. – Shepmaster Nov 16 '17 at 21:27
  • 1
    i used `Arc` and cloned `closure : write_thread` and `keys` and it started compiling. Also i removed mut from storage as it was not required with some refactoring. https://play.rust-lang.org/?gist=78782ea65eaa2b0f3ad8d2a1d6420027&version=stable – Ashish Negi Nov 16 '17 at 21:53
  • 1
    @AshishNegi that's great to hear! I see you've got some non-idiomatic Rust code, so once you have something working, you might think about posting it to [Code Review](https://codereview.stackexchange.com/questions/tagged/rust) to have someone provide feedback. – Shepmaster Nov 16 '17 at 22:04