Lifetime problem


#1
use std::marker::PhantomData;

pub trait StartableApplication<'a, 'b>
    where 'b: 'a
{
    type Application: UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

pub trait UpdateWithContext<Context> {
    fn update(&mut self, context: Context);
}

pub struct Context<'a, 'b>
    where 'b: 'a
{
    pub engine: &'a mut Engine<'b>,
}

pub struct Engine<'a> {
    phantom_data: PhantomData<&'a mut i32>,
}

impl<'b> Engine<'b> {
    pub fn update<A>(&mut self, app: &mut A)
        where for<'a> A: UpdateWithContext<Context<'a, 'b>>
    {
        let context = Context { engine: self };
        app.update(context);
    }
    
    pub fn start<A>(&mut self, app: A)
        where for<'a> A: StartableApplication<'a, 'b>
    {
        let mut app = app.start(self);
        loop {
            self.update(&mut app);
        }
    }
}

fn main() {
    println!("Hello, world!");
}

Error:

src/main.rs:38:18: 38:24 error: the trait bound `for<'a> <A as StartableApplication<'_, '_>>::Application: UpdateWithContext<Context<'a, '_>>` is not satisfied [E0277]
src/main.rs:38             self.update(&mut app);
                                ^~~~~~
src/main.rs:38:18: 38:24 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:38:18: 38:24 help: consider adding a `where for<'a> <A as StartableApplication<'_, '_>>::Application: UpdateWithContext<Context<'a, '_>>` bound

What I do wrong?

If I understood some idea wrong please correct me.


#2

Take a look at the following:

use std::marker::PhantomData;

pub trait StartableApplication<'a, 'b>
    where 'b: 'a
{
    type Application: UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

pub trait UpdateWithContext<Context> {
    fn update(&mut self, context: Context);
}

pub struct Context<'a, 'b>
    where 'b: 'a
{
    pub engine: &'a mut Engine<'b>,
}

pub struct Engine<'a> {
    phantom_data: PhantomData<&'a mut i32>,
}

impl<'b> Engine<'b> {
    pub fn update<'a, A>(&'a mut self, app: &mut A)
        where 'b: 'a, A: UpdateWithContext<Context<'a, 'b>>,
    {
        let context = Context { engine: self };
        app.update(context);
    }
    
    pub fn start<A>(&mut self, app: A)
        where for<'a> A: StartableApplication<'a, 'b>,
    {
        let mut app = app.start(self);
        loop {
            self.update(&mut app);
        }
    }
}

fn main() {
    println!("Hello, world!");
}

This gets past type-checking. Note that rustc tends to give low-quality error messages if you’re doing complicated things with lifetimes.


#3

Thanks a lot.
Could you please explain me what does <'a> means for update


#4

'a is a generic parameter ( https://doc.rust-lang.org/book/lifetimes.html ) . What exactly is your question about it?


#5

What type context in update has?


#6

I’m tempted to say you’re diving in over your head if you can’t answer that question at least partially… often, it’s a lot easier to use a bunch of Rc and RefCell rather than try to create structures and traits where multiple lifetimes are involved.

Anyway, the usage of 'a in update() makes the lifetime explicit, and ties the lifetime of self to the lifetime of the UpdateWithContext trait. This is necessary because StartableApplication takes an explicit lifetime 'a, and so StartableApplication::Application only implements UpdateWithContext for a specific lifetime.

Taking another look, that might be confusing because it’s not actually what you want. Maybe something more like this is closer?

use std::marker::PhantomData;

pub trait StartableApplication<'b>
{
    type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

pub trait UpdateWithContext<Context> {
    fn update(&mut self, context: Context);
}

pub struct Context<'a, 'b>
    where 'b: 'a
{
    pub engine: &'a mut Engine<'b>,
}

pub struct Engine<'a> {
    phantom_data: PhantomData<&'a mut i32>,
}

impl<'b> Engine<'b> {
    pub fn update<A>(&mut self, app: &mut A)
        where A: for<'a> UpdateWithContext<Context<'a, 'b>>,
    {
        let context = Context { engine: self };
        app.update(context);
    }
    
    pub fn start<A>(&mut self, app: A)
        where A: StartableApplication<'b>,
    {
        let mut app = app.start(self);
        loop {
            self.update(&mut app);
        }
    }
}

fn main() {
    println!("Hello, world!");
}

#7

What’s the difference between


pub trait StartableApplication<'a, 'b>
    where 'b: 'a
{
    type Application: UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

and


pub trait StartableApplication<'b>
{
    type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

#8

You example works, but mine not. I don’t see any difference.


#9

I added use and it does not compiles:

use std::marker::PhantomData;

pub trait StartableApplication<'b>
{
    type Application: for<'a> UpdateWithContext<Context<'a, 'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

pub trait UpdateWithContext<Context> {
    fn update(&mut self, context: Context);
}

pub struct Context<'a, 'b>
    where 'b: 'a
{
    pub engine: &'a mut Engine<'b>,
}

pub struct Engine<'a> {
    phantom_data: PhantomData<&'a mut i32>,
}

impl<'b> Engine<'b> {
    pub fn update<A>(&mut self, app: &mut A)
        where A: for<'a> UpdateWithContext<Context<'a, 'b>>,
    {
        let context = Context { engine: self };
        app.update(context);
    }
    
    pub fn start<A>(&mut self, app: A)
        where A: StartableApplication<'b>,
    {
        let mut app = app.start(self);
        loop {
            self.update(&mut app);
        }
    }
}

struct MyStartableApplication;

impl<'a> StartableApplication<'a> for MyStartableApplication {
    type Application = MyApplication;
    
    fn start(self, engine: &mut Engine<'a>) -> Self::Application {
        MyApplication::new(engine)
    }
}

struct MyApplication;

impl MyApplication {
    fn new<'a>(engine: &mut Engine<'a>) -> Self {
        MyApplication
    }
}

impl<'a, 'b> UpdateWithContext<Context<'a, 'b>> for MyApplication
    where 'b: 'a
{
    fn update(&mut self, context: Context<'a, 'b>) {
    }
}

fn main() {
    println!("Hello, world!");
}

I have solved this. This is because I added where 'b: 'a in impl UpdateWithContext for MyApplication


Lifetime question
#10

Ugh, yes, you’re right, type Application: for<'a> UpdateWithContext<Context<'a, 'b>>; isn’t usable. Not sure what I was thinking. And here I thought I knew what I was doing with lifetimes.

Your whole problem becomes a lot simpler if you get rid of the Context struct. The way you construct the context makes things really awkward because you can’t really express the lifetimes the way you want to in Rust.

Example:

use std::marker::PhantomData;

pub trait StartableApplication<'b>
{
    type Application: UpdateWithContext<Engine<'b>>;
    
    fn start(self, engine: &mut Engine<'b>) -> Self::Application;
}

pub trait UpdateWithContext<Engine> {
    fn update(&mut self, context: &mut Engine);
}

pub struct Engine<'a> {
    phantom_data: PhantomData<&'a mut i32>,
}

impl<'b> Engine<'b> {
    pub fn update<A>(&mut self, app: &mut A)
        where A: UpdateWithContext<Engine<'b>>,
    {
        app.update(self);
    }
    
    pub fn start<A>(&mut self, app: A)
        where A: StartableApplication<'b>,
    {
        let mut app = app.start(self);
        loop {
            self.update(&mut app);
        }
    }
}

struct MyStartableApplication;

impl<'a> StartableApplication<'a> for MyStartableApplication {
    type Application = MyApplication;
    
    fn start(self, engine: &mut Engine<'a>) -> Self::Application {
        MyApplication::new(engine)
    }
}

struct MyApplication;

impl MyApplication {
    fn new<'a>(engine: &mut Engine<'a>) -> Self {
        MyApplication
    }
}

impl<'a, 'b> UpdateWithContext<Engine<'b>> for MyApplication
    where 'b: 'a
{
    fn update(&mut self, context: &mut Engine<'b>) {
    }
}

fn main() {
    println!("Hello, world!");
}