error[E0597]: `new_entity2` does not live long enough

This code, with rust 1.72.1 (the latest available to me until I upgrade my OpenBSD laptop):

    fn add_uri_entity_with_uri_attribute<'a>(
        &'a self,
        transaction: Option<Rc<RefCell<Transaction<'a, Postgres>>>>,
[....]
        quote_in: Option<&str>, /*= None*/
    ) -> Result<(Entity<'a>, RelationToLocalEntity<'a>), anyhow::Error> {
[....]
        let (new_entity, new_rtle) = containing_entity_in
            .create_entity_and_add_has_local_relation_to_it(
                transaction.clone(),
                new_entity_name_in,
                observation_date_in,
                make_them_public_in,
                caller_manages_transactions_in,
            )?;
[....]
        let new_entity2 = new_entity.clone();
        new_entity2.add_text_attribute2(
            transaction.clone(),
            uri_class_template_id,
            uri_in,
            None,
            None,
            observation_date_in,
            caller_manages_transactions_in,
        )?;
        Ok((new_entity, new_rtle))
    }

...gets this error:

error[E0597]: `new_entity2` does not live long enough
   --> src/model/postgres/postgresql_database3.rs:106:9
    |
43  |       fn add_uri_entity_with_uri_attribute<'a>(
    |                                            -- lifetime `'a` defined here
...
96  |           let new_entity2 = new_entity.clone();
    |               ----------- binding `new_entity2` declared here
...
106 | /         new_entity2.add_text_attribute2(
107 | |             transaction.clone(),
108 | |             uri_class_template_id,
109 | |             uri_in,
...   |
113 | |             caller_manages_transactions_in,
114 | |         )?;
    | |         ^
    | |         |
    | |_________borrowed value does not live long enough
    |           argument requires that `new_entity2` is borrowed for `'a`
...
126 |           }
    |           - `new_entity2` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `onemodel` (bin "onemodel" test) due to previous error
        5.48 real         3.30 user         0.74 sys

...which I fail to understand. I don’t get why it thinks the value has been borrowed, or how to fix it. The current code reflects some of the things I have tried so far. I have been studying about lifetimes but am not sure that my program is using them correctly in general -- I have been mostly just following the advice of the compiler -- I’m not sure if I could somehow specify fewer lifetimes in general, in the program as a whole.

The entire program is here, with this error around line 99 of:

...and the method being called is here:

...where the last commit to the project contains this error. The code is in a state of flux, incrementally converting from commented-out Scala into Rust.

Thanks much!!

Think about what exactly this means, for a minute. In Rust, you usually use references often. References always have lifetimes. But, those lifetimes are most often not given any names. When you specify lifetimes, when you give them names, it is often because you need two lifetime parameters to be the same lifetime. So, by writing more code (lifetime names), you cause the program to have fewer distinct lifetimes.

Borrow-checking problems can be because you have distinct lifetimes that need to be the same (or need to have an ‘outlives’ relationship), or because you have lifetimes required to be the same that should be allowed to differ.

In your particular case, your function signatures are:

    fn add_uri_entity_with_uri_attribute<'a>(
        &'a self,
        transaction: Option<Rc<RefCell<Transaction<'a, Postgres>>>>,
    pub fn create_entity_and_add_has_local_relation_to_it<'a>(
        &'a self,
        transaction: Option<Rc<RefCell<Transaction<'a, Postgres>>>>,
    pub fn add_text_attribute2<'a>(
        &'a self,
        transaction: Option<Rc<RefCell<Transaction<'a, Postgres>>>>,

In both of these cases, the usage of 'a in two places says that the lifetime of the borrow of self must be the same as the lifetime in the Transaction. Because the Transaction is in a RefCell — because it could, in principle, be overwritten with a different Transaction, that lifetime is invariant — it may not be treated as if it were shorter, unlike most lifetimes.

Therefore, you cannot call Entity::add_text_attribute2 unless the Entity that is passed as self can be borrowed for 'a. Something can be borrowed for 'a only if its owner is guaranteed to keep it around, neither dropped nor moved, for 'a. But, in fact, the owner of new_entity2 is the local variable new_entity2, which will drop it when add_uri_entity_with_uri_attribute returns.

I can't say for sure without studying the entire program, and the entire program is huge so I will not be doing that, but it seems to me that your mistake is in setting these two lifetimes equal. Most of the time, when you are borrowing something to call a method on it, you do not want that borrow to last past that call, or at least, not past your use of a reference it returns. So, most of the time, it is a mistake or at least not strictly necessary to use a named lifetime on self — there are times when it is either necessary or clearer, but it is often wrong.

So, go through your program and change &'a self to &self, and see where that leads you.


A concept I personally like to use for this sort of thing is that different named lifetimes generally belong to different “layers” of your program. Loosely speaking, each lifetime describes the period over which a certain set of references remains valid, and we can call that set a layer. In most cases, at any point in your program, there's some stuff that already existed that you're borrowing (the layer below) and some stuff that you are creating that didn't exist before (the current layer). If you want to borrow anything of the current layer, you must do it with a lifetime suiting the new layer, not conflate it with the layer below.

You have:

pub struct Entity<'a> {
    db: Box<&'a dyn Database>,

(That Box is unnecessary, by the way.) This means that this 'a refers to a layer which includes “the database”. You should therefore avoid using this 'a (that is, any lifetime 'x which appears in Entity<'x>) to refer to anything that doesn't live as long as the database. (There are cases where it can be fine, but being over-cautious won't hurt.)

4 Likes

Thanks very much for your reply. I need to think about this more.

I forgot to mention: if I change:

...on line 89 of postgresql_database3.rx to "None", the problem goes away. I don't understand what dangling reference that fixes, since lifetimes are supposed to be about preventing dangling references....

I think I understand the part about "layers" (which I think of as scopes), but I need to do better at applying it here or something. I have in the past (like in my commit 9a4e8565c4 of April 24) tried removing as many lifetimes as I could and adding only what the compiler seemed to force me to add. Maybe I need to use not just 'a but 'a and 'b in some places, like here. Maybe I could restart in a branch from there, but I'm hoping the "None" clue above suggests something helpful that I can do now....
Thanks again.

Also, when I change the signature of Entity's add_text_attribute2 from "&'a self" to "&self", I get the compiler error (which I am still pondering):


error: lifetime may not live long enough
   --> src/model/entity.rs:921:9
    |
901 |     pub fn add_text_attribute2<'a>(
    |                                -- lifetime `'a` defined here
902 |         &self,
    |         ----- has type `&entity::Entity<'1>`
...
921 |         TextAttribute::new2(*self.db, transaction, id)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

I tried to do as you suggested and remove some instances of "'a" from the calling chain. Now I get:

error: lifetime may not live long enough
   --> src/model/entity.rs:911:18
    |
902 |           &self,
    |           ----- has type `&entity::Entity<'1>`
903 |           transaction: Option<Rc<RefCell<Transaction<Postgres>>>>,
    |           ----------- has type `Option<std::rc::Rc<std::cell::RefCell<sqlx::Transaction<'2, sqlx::Postgres>>>>`
...
911 |           let id = self.db.create_text_attribute(
    |  __________________^
912 | |             transaction.clone(),
913 | |             self.id,
914 | |             in_attr_type_id,
...   |
919 | |             sorting_index_in,
920 | |         )?;
    | |_________^ argument requires that `'1` must outlive `'2`

error: lifetime may not live long enough
    --> src/model/postgres/postgresql_database3.rs:1256:44
     |
1210 |         &self,
     |         - let's call the lifetime of this reference `'1`
...
1213 |         transaction_in: Option<Rc<RefCell<Transaction<Postgres>>>>,
     |         -------------- has type `Option<std::rc::Rc<std::cell::RefCell<sqlx::Transaction<'2, sqlx::Postgres>>>>`
...
1256 |         let local_tx_option = Some(Rc::new(RefCell::new(local_tx)));
     |                                            ^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`

...which baffles me. I wonder if solving the 2nd error would fix the first (since the first calls the 2nd). In the 2nd error, I don't see the connection between local_tx and transaction_in, or what is the possible dangling reference that causes the error (since I am trying to see lifetimes errors as preventing possibility of dangling references within nested scopes).

I have again put the latest code at:

and

Thanks much for even reading this far!!

One thing that really helps and is often suggested is to add this at the top of your lib.rs, main.rs, or at least the two modules you're working with:

#![warn(elided_lifetimes_in_paths)]

It may be that a lifetime is actually present in the fn signatures you're debugging, and you're not seeing it because it is elided. The warnings will tell you where a lifetime was elided from types such as Entity and Transaction in fn signatures.

Then you can add a named lifetime if one applies, or just add '_ when you know it should be elided according to the elision rules.

Because this sort of lifetime elision can cause confusion I add a deny to my lib.rs or main.rs to force myself to make them explicit:

#![deny(elided_lifetimes_in_paths)]

But since you have a lot of code you may want to use warn and just add it to the modules with the types your debugging, for now anyway.

1 Like

Thanks for the comments. I might be closer but am punting for now and just passing None instead of the transaction parameter. FWIW, here are some other resources that also seem helpful which I hope to study further:

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.