Search code examples
structrustlifetimerust-obsolete

How do I state that I want a struct which contains a reference to something which implements a trait?


Editor's note: The code presented in the question compiles as-is in Rust 1.0.

I've tried:

trait Inner {}

struct Outer<'a> {
    inner: &'a Inner,
}

but the compiler complains:

   Compiling tst v0.1.0 (file:///home/chris/rust/tst)
/home/chris/rust/tst/src/main.rs:4:14: 4:19 error: explicit lifetime bound required
/home/chris/rust/tst/src/main.rs:4   inner: &'a Inner,

Solution

  • How do I tell Rust that I want a struct which contains a reference to something which implements a trait?

    There are two ways. First, the preferred one, is to use generics:

    struct Outer<'a, T> {
        inner: &'a T,
    }
    
    impl<'a, T: Inner> Outer<'a, T> {
        // ...
    }
    

    This method is the most efficient as all function calls are statically dispatched. It's also the most type-safe one, but its disadvantage is that you will have to specify the trait bound everywhere you use Outer<T> and you won't be able to hold different implementations of Inner in the same structure at different times because T must be known in advance.

    Another way is to use a trait object:

    struct Outer<'a> {
        inner: &'a (Inner + 'a),
    }
    

    This is what you have already tried, and the error you see is caused by not specifying the lifetime bound: that + 'a thing. You need to specify a lifetime bound because traits can be implemented for structs with lifetime parameters (like Outer), and if you box such a structure into a trait object, you need a way to specify its lifetime parameter in trait object type.

    The advantage of trait objects is a fewer amount of annotations and the ability to use an arbitrary type as inner field of the same Outer value, as long it satisfies Inner bound. The downside is that you will get dynamic dispatch instead, which may be slightly less efficient. You also wouldn't be able to get back the original type of the trait object without additional machinery.