Mismatch lifetimes when moving ownership

I am running into some weird issues with lifetimes in the code below. I am a bit puzzled why there are lifetime failures as everything is passed by value which should transfer ownership and in my understanding would establish a new start of a lifetime based on the lifetime of the new owner. Would appreciate if somebody could shed some light on why this fails - and maybe how to get around the issue :slight_smile:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=664cfe6abaf78066b4176762123982ec

use std::marker::*;

trait Runnable {
    fn run(self);
}

trait DataSource<T> {
    fn get_item<'a>(&'a self) -> &'a T;
}

trait Consumer<T> {
    type Prepared: Runnable;
    fn prepare<D: DataSource<T>>(self, data_source: D) -> Self::Prepared;
}

struct ConsumerA<T> {
    _e: PhantomData<T>,
}

struct PreparedConsumerA<D: DataSource<T>, T> {
    consumer: ConsumerA<T>,
    data_source: D,
}

impl<T> ConsumerA<T> {
    fn new() -> Self {
        ConsumerA {
            _e: Default::default(),
        }
    }
}

impl<T: std::fmt::Debug> Consumer<T> for ConsumerA<T> {
    type Prepared = Box<dyn Runnable>;
    fn prepare<D: DataSource<T>>(self, data_source: D) -> Self::Prepared {
        Box::new(PreparedConsumerA {
            consumer: self,
            data_source,
        })
    }
}

impl<D: DataSource<T>, T: std::fmt::Debug> Runnable for PreparedConsumerA<D, T> {
    fn run(self) {
        println!("Consumer A got {:?}", self.data_source.get_item());
    }
}

impl Runnable for Box<dyn Runnable> {
    fn run(self) {
        (*self).run()
    }
}

impl<T> DataSource<T> for Vec<T> {
    fn get_item<'a>(&'a self) -> &'a T {
        self.first().unwrap()
    }
}

fn main() {
    let mut runnables: Vec<Box<dyn Runnable>> = Vec::new();

    let data_source1: Vec<i32> = vec![0, 1, 2, 3];
    let consumer1 = ConsumerA::new();
    runnables.push(consumer1.prepare(data_source1));

    let data_source2: Vec<i64> = vec![0, 1, 2, 3];
    let consumer2 = ConsumerA::new();
    runnables.push(consumer2.prepare(data_source2));

    for r in runnables.iter() {
        r.run();
    }
}

Trait objects have a lifetime parameter that represents an upper bound on their lifetime.

type Prepared = Box<dyn Runnable>;

This is equivalent to

type Prepared = Box<dyn Runnable + 'static>;

To make this work, your Consumer trait needs to be generic over lifetimes.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b9bf0df917e27eb9ac6cbedb156cb9f5

(there is still an error because it is not currently possible to use fn(self) functions in a dynamic context as that would require unsized locals, which are unstable)

If we consider that 'a : 'b is 'a ≥ 'b, I'd say that it's more of a lower bound

And here is a version that uses fn run (self: Box<Self>) as the signature of the Trait's method in order to make your code work: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b8d796f4df775a3893996076cf249549

Thanks for your help, ExpHP and Yandros. I didn't know about the self: Box<Self> syntax, learning something new every day :slight_smile:

1 Like