Problem with lifetimes and trait objects

I'm having trouble writing some code with trait objects. Let me explain with some code:

// Assume `RenderContext` is some trait that provides functionality for standard drawing 
// and text layout, although what it actually does isn't important for the problem.
trait Drawable<RC: RenderContext> {
    // Draw the scene
    fn draw(&mut self, rc: &mut RC);
}
// I want to make `Drawable` into a trait object, so I can store a heterogeneous collection of 
// `Drawable`s in a `Vec`.

struct Point { x: f64, y: f64 }

struct RedCircle { center: Point, radius: f64 }
impl<RC: ...> Drawable<RC> for RedCircle { ... }

struct BlueRect { top_left: Point, btm_right: Point }
impl<RC: ...> Drawable<RC> for BlueRectangle { ... }
// Imagine we have an impl of `RC` that we want to use (we must specify this or the
// trait objects are not fully specified).
let things: Vec<Box<dyn Drawable<ConcreteRC>> = vec![
  Box::new(RedCircle { .. }), 
  Box::new(BlueRectangle { .. })
];

let rc = ConcreteRC::get_me_somehow();
for drawable in things.iter_mut() {
    drawable.draw(&mut rc);
}

Now this all works fine, great! I have actually built all this and got it to draw. The problem comes when ConcreteRC has a lifetime parameter (it is actually ConcreteRC<'a>). Now, let's imagine that every frame a new ConcreteRC is created and used to draw the scene. Each of these ConcreteRCs has a different type. This is because each has a different lifetime (the lifetime of that particular frame). Now for our implementation we don't actually care about the lifetime because we know the lifetime lasts longer than the &mut borrow, but rustc will not allow us to erase the lifetime, which we would need to do in order to be able to specify the type of the trait object.

So what I need is some way to say "even though the type I want to use in my trait object is generic, it's actually only generic in the lifetime. Moreover, I know that it will always live long enough, so it's safe to use the same vtable for each frame". Does anyone know how I could change what I'm doing to make this work, other than unsafely extending the lifetime to 'static so that it isn't generic any more (I'd also need to stop the user storing the RenderContext somehow, maybe by stopping them having a way to create one).

Try this out: Box<dyn for<'a> Drawable<ConcreteRC<'a>>. It means that it should implement the following infinite list of traits:

  1. Drawable<ConcreteRC<'lifetime1>>
  2. Drawable<ConcreteRC<'lifetime2>>
  3. Drawable<ConcreteRC<'lifetime3>>
  4. ...
  5. Drawable<ConcreteRC<'static>>

Where the list is infinitely long and goes through all lifetimes.

3 Likes

Ooh I did not know that I can say "forall lifetimes" on my trait object. Will experiment.

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.