Trait object can not be shared between threads safely

These simple code can not compile.

trait A {}

struct B;

impl A for B {}

struct C {
    a: Box<dyn A>
}
impl C {
    fn f(&self) -> i32 {
        1
    }
}

let b: Box<dyn A> = Box::new(B);
let c = C { a: b };
thread::scope(|scope| {
    scope.spawn(|| c.f());
    scope.spawn(|| c.f());
})

[E0277] Error: `(dyn A + 'static)` cannot be shared between threads safely
   ╭─[command_257:1:1]
   │
 2 │     scope.spawn(|| c.f());
   ·           ──┬── ─┬──┬───  
   ·             ╰───────────── required by a bound introduced by this call
   ·                  │  │     
   ·                  ╰──────── note: required because it's used within this closure
   ·                     │     
   ·                     ╰───── `(dyn A + 'static)` cannot be shared between threads safely
   · 
   · Note: note: required because it appears within the type `Box<(dyn A + 'static)>`note: required because it appears within the type `C`
───╯

I thought c is just immutable shared between threads, it won't be changed by any thread. I can not figure out why it says "unsafe". What should I do?

Change Box<dyn A> to Box<dyn A + Sync>.

For concrete types the compiler automatically handles Send and Sync bounds, but trait objects can't be checked statically. You have to manually specify which auto traits your trait object requires.

In this case you are sharing references to the trait object between threads, so you need Sync to be implemented for your trait object.

Playground

#![allow(dead_code)]
trait A {}

struct B;

impl A for B {}

struct C {
    a: Box<dyn A + Sync>,
}

impl C {
    fn f(&self) -> i32 {
        1
    }
}

fn main() {
    let b: Box<dyn A + Sync> = Box::new(B);
    let c = C { a: b };
    std::thread::scope(|scope| {
        scope.spawn(|| c.f());
        scope.spawn(|| c.f());
    })
}
7 Likes

Usually you need both Send + Sync: Box<dyn A + Send + Sync>

5 Likes