Box with a trait object requires static lifetime?

You can write code blocks by enclosing them with triple backticks ```:

```rust
struct Holder {
    ...
```

giving:

struct Holder {
    objects: Vec<Box<dyn Object>>,
}

impl Holder {
    fn add_object<T : Object> (
        self: &'_ mut Self,
        object: T,
    )
    {
        self.objects
            .push(Box::new(object))
        ;
    }
}

Now, regarding the issue at hand, Box<dyn Trait> is just sugar for Box<dyn Trait + 'static>, meaning that the trait object / type erased element has a : 'static bound (meaning that it is not borrowing any local, and thus the value is guarantee not to dangle at any time, which makes it usable for as long as we wish. If we had, for instance, T = &'a str, then we would be borrowing a string for some lifetime 'a, after which the string could be freed, which would make using such reference / pointer unsound. So when 'a != 'static, it would be unsound to say that such a pointer lives for 'static, which is what dyn Trait + 'static says.

You can solve this in two ways:

  • either you forbid such short-lived references (recommended for the sake of simplicity). That's what adding T : 'static as the compiler suggests does. When 'a != 'static, for instance, you will not be able to use T = &'a str.

  • or you decide that you do not need such a long-lived value; that borrowing for the lifetime 'a is fine. You must then replace every occurence of Box<dyn Trait + 'static> (or the equivalent Box<dyn Trait>) by a Box<dyn Trait + 'lifetime>, by adding / infecting everything with a <'lifetime> lifetime parameter. This way, you can loosen your function into taking a T : 'lifetime for some generic 'lifetime, which will make lifetime "become" 'a when using T = &'a str:

trait Object {}

struct Holder<'lifetime> {
    objects: Vec<Box<dyn Object + 'lifetime>>,
}

impl<'lifetime> Holder<'lifetime> {
    fn add_object<T : Object + 'lifetime> (
        self: &'_ mut Self,
        object: T,
    )
    {
        self.objects
            .push(Box::new(object))
        ;
    }
}
  • Playground (compiles just fine).

  • (The first option is just the special case of this more general / generic code, whereby 'lifetime is chosen equal to 'static).

  • Note that infecting Box<dyn Trait + ... > with a 'lifetime parameter greatly reduces the benefits and thus point of using a Box (owning pointer) for the indirection: at that point, using &'lifetime dyn Trait (sugar for &'lifetime (dyn Trait + 'lifetime)) would not be that much less ergonomic, and would avoid needing to Box things around (playground).

25 Likes