Relative Lifetime Issue

I've tried and failed to fix this relative lifetime issue. Lifetime 'a will outlive 'b, but I don't know how to specify it. Hopefully the playground link below works. How can I specify the relative lifetimes?

struct BacktestLib<'a>{
    pub string_instances: Vec<StringHolder<'a>>,
    pub df_source: String
}

impl Default for BacktestLib<'_>{
    fn default() -> Self {
        Self {
            string_instances: Vec::new(),
            df_source: String::from(""),
        }
    }
}

impl BacktestLib<'_>{
    fn backtestlib(&mut self){
        let mut tp = StringHolder{string: None};

        tp.string = Some(&self.df_source);

        self.string_instances.push(tp);
    }
}
pub struct StringHolder<'b>{
    pub string : Option<&'b String>,
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
  --> src/lib.rs:21:9
   |
16 |     fn backtestlib(&mut self){
   |                    ---------
   |                    |
   |                    let's call the lifetime of this reference `'1`
   |                    has type `&mut BacktestLib<'2>`
...
21 |         self.string_instances.push(tp);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`

error: could not compile `playground` (lib) due to previous error

Hello self-referential-data-type, my old friend
I've come to talk with you again!
Because a vision softly   creeping
left its seeds while I was   sleeping.
And the vision   that was planted in my brain
still remains
within the sound of […] …ah!, where was I !?
Right, answering the question!

7 Likes

No, it won't. Consider what happens when an instance of BacktestLib is moved.

Is it inevitable that I move BacktestLib? Maybe I should be putting instances of StringHolders into a Box?

Moving BacktestLib is just a way this would create unsafety, and putting it in a box won't tell anything to the compiler. The fundamental problem is that backtestlib only has temporary access to the BacktestLib, but its implementation needs the df_source field to be borrowed (and thus the whole BacktestLib) until the BacktestLib goes out of scope. The only kinda way to require this is by writing a function that takes a &'a mut BacktestLib<'a>, which is generally a big red flag you're doing something wrong/impossible. And in fact calling this function will result in the whole struct being mutably borrowed until it goes out of scope, effectively making that function call the last use of your struct.

1 Like

That doesn't help. The borrow checker doesn't – can't – know about heap-allocation. A reference derived from a Box has exactly the same relation (in terms of lifetimes) to the Box as a reference pointing to an inline container's element would have.

Just don't create self-referential types, and you will live a long, happy life.

4 Likes

I think the intent of what I'm doing is clear. What specific mods or reorganizations to this code should I make to avoid the lifetime issue?

You could change df_source and StringHolder::string to use Rc<String>

Thanks, will check that out.

It's not really clear to me. What do you expect to happen when df_source is overwritten? (If you thought this could happen automatically -- it cannot, Rust has no garbage collector.)

So I can only guess at what you meant. Perhaps just have a Vec<String> and push a Clone of the current df_source in backtestlib.


If you go the Rc or Arc route, use Rc<str> or Arc<str>, not Rc<String>. You'll do less allocation but gain reference counting overhead.

1 Like

Just to be absolutely certain: In case you didn’t click the link in my reply, well, click the link :wink: It almost sounds as if you might have not clicked it; since the linked reply contains a whole list of approaches, and your follow-up question here picks up on none of those suggestions…

1 Like

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.