Borrowed value does not live long enough, need 'static for Diesel model

Hi everybody,

I have a Vec of diesel model instances, and I try to use it to get related datas and next convert it into my business entities.

The problem is I encounter problems with borrowing, and I missed something...

Code
    async fn get_all_screen_elements(
        &self,
        db_connection: &DbConnection,
        screen: &'static models::Screens,
    ) -> Result<Vec<ScreenElement>, ErrorEntity> {
        let mut screen_elements: Vec<ScreenElement> = vec![];

        for media in self
            .read_media_screen_elements_for_screen(db_connection, screen)
            .await?
        {
            screen_elements.push(media.into());
        }

        for estate in self
            .read_estate_screen_elements_for_screen(db_connection, screen)
            .await?
        {
            let photos = self
                .read_estate_photo_screen_elements_for_estate_screen_element(db_connection, &estate)
                .await?;
            screen_elements.push(estate.into_entity(photos));
        }

        Ok(screen_elements)
    }

    // Signature of method :
    async fn read_estate_photo_screen_elements_for_estate_screen_element(
        &self,
        db_connection: &DbConnection,
        template: &'static models::EstateTemplates,
    ) -> Result<Vec<models::EstatePhotoTemplates>, ErrorEntity>;
Console build errors
error[E0597]: `estate` does not live long enough
   --> src/infrastructure/repositories/database_repository.rs:419:93
    |
418 |               let photos = self
    |  __________________________-
419 | |                 .read_estate_photo_screen_elements_for_estate_screen_element(db_connection, &estate)
    | |_____________________________________________________________________________________________^^^^^^^- argument requires that `estate` is borrowed for `'static`
    |                                                                                               |
    |                                                                                               borrowed value does not live long enough
...
422 |           }
    |           - `estate` dropped here while still borrowed

error[E0505]: cannot move out of `estate` because it is borrowed
   --> src/infrastructure/repositories/database_repository.rs:421:34
    |
418 |               let photos = self
    |  __________________________-
419 | |                 .read_estate_photo_screen_elements_for_estate_screen_element(db_connection, &estate)
    | |_____________________________________________________________________________________________-------- argument requires that `estate` is borrowed for `'static`
    |                                                                                               |
    |                                                                                               borrow of `estate` occurs here
420 |                   .await?;
421 |               screen_elements.push(estate.into_entity(photos));
    |                                    ^^^^^^ move out of `estate` occurs here

Some errors have detailed explanations: E0505, E0597.
For more information about an error, try `rustc --explain E0505`.
warning: `adbridge-api` (lib) generated 4 warnings
error: could not compile `adbridge-api` due to 2 previous errors; 4 warnings emitted

I have pu 'static on screen: &'static models::Screens and template: &'static models::EstateTemplates because I have to use them multiple times in the method.

Could someone explain what do I missed please ?

Thank you:slight_smile:

You don't want 'static in either of these functions. Borrowing something for 'static means borrowing it for the rest of the program.

You can't borrow estate for 'static in self.read_estate_photo_screen_elements_for_estate_screen_element(db_connection, &estate).await?; because estate is not valid for the rest of the program; it's dropped as soon as that for loop has finished.

Even if you could, you couldn't then call estate.into_entity(photos), because it requires moving estate. But in this case, estate would have been borrowed for the rest of the program which prevents moving it.

Yes, I don't want static, but I can't found another solution...
I can't clone a Diesel model, and I can't create a specific lifetime (I have an error that tell me it must be 'static and nothing else...)

So I don't know how to do. Do I develop it in the bad way ?
What could I do to make my method working ?

Here the simplified code (I have replace business entities with String to make it less heavy to understand) to let you see how it was before I go with 'static :

Cargo.toml
[package]
name = "test-example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
r2d2 = "0.8.9"
r2d2-diesel = "1.0.0"

[dependencies.diesel]
version = "1.4.8"
features = ["postgres", "r2d2"]

[dependencies.diesel_codegen]
version = "0.16.1"
features = ["postgres"]

[dependencies.rocket]
version = "0.5.0-rc.1"
features = ["json", "secrets"]

[dependencies.rocket_sync_db_pools]
version = "0.1.0-rc.1"
default-features = false
features = ["diesel_postgres_pool"]
schema.rs
table! {
    estate_photo_templates (id) {
        id -> Int4,
        // ...
        estate_templates_id -> Int4,
        created_at -> Timestamptz,
        updated_at -> Timestamptz,
    }
}

table! {
    estate_templates (id) {
        id -> Int4,
        // ...
        screens_id -> Int4,
        created_at -> Timestamptz,
        updated_at -> Timestamptz,
    }
}

table! {
    media_templates (id) {
        id -> Int4,
        // ...
        screens_id -> Int4,
        created_at -> Timestamptz,
        updated_at -> Timestamptz,
    }
}

table! {
    screens (id) {
        id -> Int4,
        // ...
        created_at -> Timestamptz,
        updated_at -> Timestamptz,
    }
}

joinable!(estate_photo_templates -> estate_templates (estate_templates_id));

allow_tables_to_appear_in_same_query!(
    estate_photo_templates,
    estate_templates,
);

models.rs
use crate::schema::*;

#[derive(Identifiable, Insertable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(EstateTemplates)]
#[table_name = "estate_photo_templates"]
pub struct EstatePhotoTemplates {
    pub id: i32,
    // ...
    pub estate_templates_id: i32,
}

impl Into<String> for EstatePhotoTemplates {
    fn into(self) -> String {
        todo!()
    }
}

#[derive(Identifiable, Insertable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(Screens)]
#[table_name = "estate_templates"]
pub struct EstateTemplates {
    pub id: i32,
    // ...
    pub screens_id: i32,
}

impl EstateTemplates {
    pub fn into_entity(self, db_photos: Vec<EstatePhotoTemplates>) -> String {
        let mut photos: Vec<String> = vec![];

        for p in db_photos {
            photos.push(p.into());
        }

        todo!()
    }
}

#[derive(Identifiable, Insertable, Queryable, Associations, PartialEq, Debug)]
#[belongs_to(Screens)]
#[table_name = "media_templates"]
pub struct MediaTemplates {
    pub id: i32,
    // ...
    pub screens_id: i32,
}

impl Into<String> for MediaTemplates {
    fn into(self) -> String {
        todo!()
    }
}

#[derive(Identifiable, Queryable, PartialEq, Debug)]
#[table_name = "screens"]
pub struct Screens {
    pub id: i32,
    // ...
}
lib.rs
#[macro_use]
extern crate diesel;
extern crate diesel_codegen;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate rocket;

pub mod models;
pub mod schema;

main.rs
use diesel::prelude::*;
use rocket_sync_db_pools::{database, diesel};

use test_example::{models, schema};

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

#[database("arius_adbridge")]
pub struct DbConnection(diesel::PgConnection);

pub struct DatabaseRepository {}

impl DatabaseRepository {
    async fn get_all_screen_elements(
        &self,
        db_connection: &DbConnection,
        screen: models::Screens,
    ) -> Result<Vec<String>, String> {
        let mut screen_elements: Vec<String> = vec![];

        for media in self
            .read_media_screen_elements_for_screen(db_connection, screen)
            .await?
        {
            screen_elements.push(media.into());
        }

        for estate in self
            .read_estate_screen_elements_for_screen(db_connection, screen)
            .await?
        {
            let photos = self
                .read_estate_photo_screen_elements_for_estate_screen_element(db_connection, estate)
                .await?;
            screen_elements.push(estate.into_entity(photos));
        }

        Ok(screen_elements)
    }

    async fn read_media_screen_elements_for_screen(
        &self,
        db_connection: &DbConnection,
        screen: models::Screens,
    ) -> Result<Vec<models::MediaTemplates>, String> {
        let screen_id = screen.id;

        match db_connection
            .run(move |conn| {
                models::MediaTemplates::belonging_to(&screen)
                    .select((
                        schema::media_templates::id,
                        // ...
                        schema::media_templates::screens_id,
                    ))
                    .load::<models::MediaTemplates>(conn)
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate screen element for screen '{}' : {}",
                screen_id, e
            )),
        }
    }

    async fn read_estate_screen_elements_for_screen(
        &self,
        db_connection: &DbConnection,
        screen: models::Screens,
    ) -> Result<Vec<models::EstateTemplates>, String> {
        let screen_id = screen.id;

        match db_connection
            .run(move |conn| {
                models::EstateTemplates::belonging_to(&screen)
                    .select((
                        schema::estate_templates::id,
                        // ...
                        schema::estate_templates::screens_id,
                    ))
                    .load::<models::EstateTemplates>(conn)
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate screen element for screen '{}' : {}",
                screen_id, e
            )),
        }
    }

    async fn read_estate_photo_screen_elements_for_estate_screen_element(
        &self,
        db_connection: &DbConnection,
        template: models::EstateTemplates,
    ) -> Result<Vec<models::EstatePhotoTemplates>, String> {
        let template_id = template.id;

        match db_connection
            .run(move |conn| {
                models::EstatePhotoTemplates::belonging_to(&template)
                    .select((
                        schema::estate_photo_templates::id,
                        // ...
                        schema::estate_photo_templates::estate_templates_id,
                    ))
                    .load::<models::EstatePhotoTemplates>(conn)
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate photo screen element for estate screen element '{}' : {}",
                template_id, e
            )),
        }
    }
}

Why not? It might not be the cleanest solution, but it should at least be possible.

I think this is one aspect that can make diesel a little awkward in async contexts. Things like DbConnection::run and tokio::spawn require 'static so you can't use borrows the way you would like to. Here are three options I could think of

  1. Use owned values and return them after you're done with them. This is probably the most inconvenient one and would look something like
    async fn read_estate_screen_elements_for_screen(
        &self,
        db_connection: &DbConnection,
        screen: models::Screens,
    ) -> Result<(Vec<models::EstateTemplates>, models::Screens), String> {
        let screen_id = screen.id;

        match db_connection
            .run(move |conn| {
                let templates = models::EstateTemplates::belonging_to(&screen)
                    .select((
                        schema::estate_templates::id,
                        // ...
                        schema::estate_templates::screens_id,
                    ))
                    .load::<models::EstateTemplates>(conn)?;
                QueryResult::Ok((templates, screen))
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate screen element for screen '{}' : {}",
                screen_id, e
            )),
        }
    }
  1. Just work with ids. Whether you're using i32s or Uuids, your ids should be Copy which means you can just copy them into closures. This would require the most refactoring on your part and could make some queries more verbose or harder to understand. It might look something like
    async fn read_estate_screen_elements_for_screen(
        &self,
        db_connection: &DbConnection,
        screen: &models::Screens,
    ) -> Result<Vec<models::EstateTemplates>, String> {
        let screen_id = screen.id;

        match db_connection
            .run(move |conn| {
                schema::estate_templates::table
                    .select((
                        schema::estate_templates::id,
                        // ...
                        schema::estate_templates::screens_id,
                    ))
                    .filter(schema::estate_templates::screens_id.eq(screen_id))
                    .load::<models::EstateTemplates>(conn)
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate screen element for screen '{}' : {}",
                screen_id, e
            )),
        }
    }
  1. Use Arcs instead of cloning. Probably the easiest option.
    async fn read_estate_screen_elements_for_screen(
        &self,
        db_connection: &DbConnection,
        screen: std::sync::Arc<models::Screens>,
    ) -> Result<Vec<models::EstateTemplates>, String> {
        let screen_id = screen.id;

        match db_connection
            .run(move |conn| {
                models::EstateTemplates::belonging_to(screen.as_ref())
                    .select((
                        schema::estate_templates::id,
                        // ...
                        schema::estate_templates::screens_id,
                    ))
                    .load::<models::EstateTemplates>(conn)
            })
            .await
        {
            Ok(e) => Ok(e),
            Err(e) => Err(format!(
                "Cannot retrieve estate screen element for screen '{}' : {}",
                screen_id, e
            )),
        }
    }

There might be other options as well. I know there are some other crates that aim to make async diesel more convenient to use, but I'm not sure if they help with this issue.

1 Like

Thank you a lot ! :smiley:

I was thinking about Arc but was not sure how to use it here.
I have implemented Arc solution, and will see if it is great or not. Else I will go on your second solution.

1 Like

Just to be clear here, nothing in diesel requires a `'static' lifetime. This requirement seems to come from something elese.

Neither this is required by diesel. In fact you control if "models" are Clone or not, as you define the corresponding struct. You can simply put a #[derive(Clone)] onto these structs as well.

No I can't
I have already tryied to derive 'Clone' but I get an error, that's why I was blocked :confused:

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.