Lifetimes and ownership with traits

Viewed 61

I am building a Rocket app and want it to manage some objects for me. For that, they need to be Send and Sync. Here's a minimal contrived example that shows the error I am getting (Playground):

trait MyTrait {
    fn foo(&self) {}
}

struct TraitThing {}

impl MyTrait for TraitThing {
    fn foo(&self) {
        println!("Foo!");
    }
}

struct Thing {
    tt: &'static (dyn MyTrait + Send + Sync),
}

impl Thing {
    fn with_trait(tt: &'static (dyn MyTrait + Send + Sync)) -> Self {
        Self { tt }
    }
}

fn main() {
    let tt = TraitThing {};
    let thing = Thing::with_trait(&tt);
    thing.tt.foo();
}

I probably don't understand lifetimes of 'static. Ideally, I want Thing to own tt, but as far as I understand, since my TraitThing isn't Sized (and probably won't ever be), it cannot be a trait object, so it must be passed by reference.

So, how do I solve this while keeping the Send and Sync traits?

Thanks!

Additional note: I've been reading a lot about lifetimes and traits in the Rust book and other places, but I'd appreciate further reading material.

2 Answers

You need to use Box:

trait MyTrait {
    fn foo(&self) {}
}

struct TraitThing {}

impl MyTrait for TraitThing {
    fn foo(&self) {
        println!("Foo!");
    }
}

struct Thing {
    tt: Box<dyn MyTrait + Send + Sync>,
}

impl Thing {
    fn with_trait(tt: Box<dyn MyTrait + Send + Sync>) -> Self {
        Self { tt }
    }
}

fn main() {
    let tt = TraitThing {};
    let thing = Thing::with_trait(Box::new(tt));
    thing.tt.foo();
}

Playground

You can use a Box but it will allocate to the Heap and use dynamic dispatch which is not useful in your case. It is useful when you want a list of different type that implements a similar Trait like :

struct Thing {
    tt: Vec<Box<dyn MyTrait + Send + Sync>>,
}

But in your case you only have one element so you can use Generics in this setup:

trait MyTrait {
    fn foo(&self) {}
}

struct TraitThing {}

impl MyTrait for TraitThing {
    fn foo(&self) {
        println!("Foo!");
    }
}

struct Thing<T>
where
    T: MyTrait + Send + Sync
{
    tt: T,
}

impl<T> Thing<T> 
where
    T: MyTrait + Send + Sync
{
    fn with_trait(tt: T) -> Self {
        Self { tt }
    }
}

fn main() {
    let tt = TraitThing {};
    let thing = Thing::with_trait(tt);
    thing.tt.foo();
}
Related