How to resolve "error[E0277]: `T` cannot be shared between threads safely"?

I am new to Rust and want to explore coding with an ECS while learning Rust.

Here is my code

use specs::{Component, DenseVecStorage, System, ReadStorage};

trait TradeableResource{}

#[derive(Component)]
struct Money(f64);

#[derive(Component)]
struct Food(f64);

impl TradeableResource for Food{}

#[derive(Component)] //compiler does not like this line
struct MarketMaker<T:TradeableResource>{
    lot_size:T
}

#[derive(Component)]
struct FoodOffer {
    lot_size:f64,
    price:f64,
}

struct SetOffers;

impl<'a> System<'a> for SetOffers {
    type SystemData = (
        ReadStorage<'a, Food>,
        ReadStorage<'a, MarketMaker<Food>>
    );

    fn run(&mut self, food: Self::SystemData) {

    }
}

fn main() {
    println!("Hello, world!");
}

The compiler is not happy that I want to make this struct a component. Full error output:

error[E0277]: `T` cannot be shared between threads safely
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^ `T` cannot be shared between threads safely
   |
   = help: within `MarketMaker<T>`, the trait `std::marker::Sync` is not implemented for `T`
help: consider further restricting this bound with `+ std::marker::Sync`
  --> src\main.rs:14:22
   |
14 | struct MarketMaker<T:TradeableResource>{
   |                      ^^^^^^^^^^^^^^^^^
   = note: required because it appears within the type `MarketMaker<T>`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<MarketMaker<T>>`
   = note: required because it appears within the type `alloc::raw_vec::RawVec<MarketMaker<T>>`
   = note: required because it appears within the type `std::vec::Vec<MarketMaker<T>>`
   = note: required because it appears within the type `specs::storage::storages::DenseVecStorage<MarketMaker<T>>`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: `T` cannot be sent between threads safely
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^ `T` cannot be sent between threads safely
   |
   = help: within `MarketMaker<T>`, the trait `std::marker::Send` is not implemented for `T`
help: consider further restricting this bound with `+ std::marker::Send`
  --> src\main.rs:14:22
   |
14 | struct MarketMaker<T:TradeableResource>{
   |                      ^^^^^^^^^^^^^^^^^
   = note: required because it appears within the type `MarketMaker<T>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<MarketMaker<T>>`
   = note: required because it appears within the type `alloc::raw_vec::RawVec<MarketMaker<T>>`
   = note: required because it appears within the type `std::vec::Vec<MarketMaker<T>>`
   = note: required because it appears within the type `specs::storage::storages::DenseVecStorage<MarketMaker<T>>`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

According to the error output, there is a problem in the derive macro for Component right?
Does this mean that the library (specs) cannot support generic components? Or is there some (safe) way to make the compiler happy?

Hi,

from what I can see it seems that the derive macro Component requires the structure it is applied to to be Send. As you are passing a generic parameter T, the compiler can only verify that this requirement is upheld by T if T would also be restricted to be Send.

A straightforward solution would be to add this constraint to your definition like so:

#[derive(Component)]
struct MarketMaker<T: TradeableResource + Send>{
    lot_size:T
}

Upon re-reading the error message, it seems I need further trait bounds. So

#[derive(Component)]    struct MarketMaker<'a, T:TradeableResource + std::marker::Send + std::marker::Sync>{
    lot_size:T
}

#[derive(Component)]
struct FoodOffer {
    lot_size:f64,
    price:f64,
}

struct SetOffers;

impl<'a> System<'a> for SetOffers {
    type SystemData = (
        ReadStorage<'a, Food>,
        ReadStorage<'a, MarketMaker<'a, Food>>
    );

    fn run(&mut self, food: Self::SystemData) {

    }
}

Seems to be a step in the right direction but now I have lifetime constraint issues.

error[E0392]: parameter `'a` is never used
  --> src\main.rs:14:20
   |
14 | struct MarketMaker<'a, T:TradeableResource + std::marker::Send + std::marker::Sync>{
   |                    ^^ unused parameter
   |
   = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

error[E0478]: lifetime bound not satisfied
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^
   |
note: lifetime parameter instantiated with the lifetime `'a` as defined on the impl at 14:20
  --> src\main.rs:14:20
   |
14 | struct MarketMaker<'a, T:TradeableResource + std::marker::Send + std::marker::Sync>{
   |                    ^^
   = note: but lifetime parameter must outlive the static lifetime
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 14:20...
  --> src\main.rs:14:20
   |
14 | struct MarketMaker<'a, T:TradeableResource + std::marker::Send + std::marker::Sync>{
   |                    ^^
note: ...so that the types are compatible
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^
   = note: expected  `specs::world::comp::Component`
              found  `specs::world::comp::Component`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `specs::storage::storages::DenseVecStorage<MarketMaker<'_, T>>` will meet its required lifetime bounds
  --> src\main.rs:13:10
   |
13 | #[derive(Component)]
   |          ^^^^^^^^^
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Thanks, I was writing my reply when you sent this. Looks like I needed Sync also. But then lifetimes are a problem unfortunately.

Hi,
but why do you introduce the lifetime 'a for the MarketMaker ?

struct MarketMaker<T: TradeableResource + Send + Sync> {
    lot_size:T
}

should work just fine ?

If you really like to stay with the lifetime: the compiler complains that you defined the lifetime as a generic parameter but you did not use it inside your structure. To fix this you could use PhantomData using the lifetime, but first I'd like to understand why you introduced the lifetime parameter at all :wink:

struct MarketMaker<'a, T: TradeableResource + Send + Sync> {
   lot_size:T,
   _phantom: &'a core::marker::PhantomData<()>,
}

&'a PhantomData<()> is not a ZST. To make it zero cost it would be better to have PhantomData<&'a ()> instead.

May I ask why it's different?

Because no references in Rust is zero sized, including reference to ZST like &(). &T has same size as the underlying machine architecture's pointer size if the T has statically known size, or can be larger if we don't know its size at compile time.

PhantomData<T> is a special type built in to the language itself. All those static analysis the compiler provides including type check and borrow check treat it as same as T. But in runtime, it doesn't occupies any space regardless of the T.

If I do not add the lifetime parameter, I get told "the parameter type T may not live long enough". Given the simplicity of my code, I think this is a requirement of the specs library. I have now added 'static lifetime, since all values of any types used for T will only contain owned values. Sound right?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.