Help with lifetime error

I am not able to implement Context for AuthContext here

link to playground

use std::ops::Deref;

pub trait Context<'a>{
   fn db(&self) -> &'a Connection;
   fn config(&self) -> &'a AppConfig;
}

struct AppConfig {}
struct Connection {}
struct PooledConnection(pub Connection);

impl Deref for PooledConnection {
    type Target = Connection;

    fn deref(&self) -> &Connection {
        &self.0
    }
}

pub struct AuthContext<'a> {
    pub db: PooledConnection,
    pub config: &'a AppConfig,
}

impl<'a, 'b: 'a> Context<'a> for  AuthContext<'b> {

    fn db(&self) -> &'a Connection {
        &self.db
    }

    fn config(&self) -> &'a AppConfig {
        &self.config
    }
}
fn do_it_with_context(context: &dyn Context){
    
}

pub fn main() {
    let cfg = AppConfig{};
    let con = Connection{};
    let ac = AuthContext{db: PooledConnection(con), config: &cfg};
    do_it_with_context(&ac);
}

edit-1: formatted code

Your trait says that reference returned by db() outlives self, and therefore is never a value stored in self (it doesn't state that explicitly, but in practice there's no other logical possibility).

However, your implementation returns self.db, which is a reference to an object that could be destroyed together with self.

You need to make pooled connection live longer than auth context, ie. store it elsewhere in a more permanent place. OR make db() not promise such a long lifetime unrelated to self in the trait.

2 Likes

@kornel thank you. I think you are correct.
@dylan.dpc helped with this on discord. I am posting here for future reference.

Full solution here

use std::ops::Deref;

pub trait Context<'a>{
   fn db(&'a self) -> &'a Connection;
   fn config(&'a self) -> &'a AppConfig;
}

pub struct AppConfig {}
pub struct Connection {}
pub struct PooledConnection(pub Connection);

impl Deref for PooledConnection {
    type Target = Connection;

    fn deref(&self) -> &Connection {
        &self.0
    }
}

pub struct AuthContext<'a> {
    pub db: PooledConnection,
    pub config: &'a AppConfig,
}

impl<'b, 'a: 'b> Context<'a> for  AuthContext<'b> {

    fn db(&'a self) -> &'a Connection {
        &self.db
    }

    fn config(&'a self) -> &'a AppConfig {
        &self.config
    }
}

fn do_it_with_context(_context: &dyn Context){
}

pub fn main() {
    let cfg = AppConfig{};
    let con = Connection{};
    let ac = AuthContext{db: PooledConnection(con), config: &cfg};
    do_it_with_context(&ac);
}
1 Like
pub trait Context<'a>{
   fn db(&'a self) -> &'a Connection;
   fn config(&'a self) -> &'a AppConfig;
}

That doesn't make sense. You probably meant:

pub trait Context{
   fn db(&self) -> &Connection;
   fn config(&self) -> &AppConfig;
}

because <'a> on the trait means it's something that had to exist before self has been created, but then you say that lifetime is also as short as lifetime of self, so in the end you gained nothing.

2 Likes
impl<'b, 'a: 'b> Context<'a> for  AuthContext<'b> {

    fn db(&'a self) -> &'a Connection {
        &self.db
    }

    fn config(&'a self) -> &'a AppConfig {
        &self.config
    }
}

Since Self = AuthContext<'b>, the fact that we have &'a self implies that 'b : 'a. With your added 'a : 'b, that means that 'a and 'b are equal, so I'd remove 'b altogether for simplicity:

impl<'a> Context<'a> for  AuthContext<'a> {

    fn db(&'a self) -> &'a Connection {
        &self.db
    }

    fn config(&'a self) -> &'a AppConfig {
        &self.config
    }
}

Note, by the way, that your borrow will thus last for the whole lifetime of the object (even if you drop result), which means you won't be able to ever mutate it afterwards; thus it does look to me like this is still not what you'd want, c.f. @kornel's remarks

@kornel @Yandros you are right. I had to change the trait Context as per @kornel's suggestion. In fact that is what I wanted.

Finally here is what it looked like

edit-1: added playground link

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.