How do I use a struct field with a lifetime<'a> correctly?

Hi,

I have a lifetime and borrow issue with a trait function acting on a struct.

Shortened code:

struct PwmGuiData<'a> {
    current_setting: Result<PwmSetting, PwmSettingsError>,
    pwm: Result<Pwm<'a>, PwmSettingsError>,
}

trait PwmGui<'a> {
    fn new() -> Self;
    fn save_settings(&mut self) -> Result<(), PwmConfigError>;
    fn pwm_from_setting(&'a mut self);
}

impl<'a> PwmGui<'a> for PwmGuiData<'a> {
    fn new() -> Self {...}
    fn save_settings(&mut self) -> Result<(), PwmConfigError> {...}
    fn pwm_from_setting(&'a mut self) {
       self.pwm = function(self.current_setting)
   }
}

fn main() -> Result<(), PwmConfigError> {
    let mut settings = PwmGuiData::new();
    settings.pwm_from_setting()
    settings.save_settings()
}

I get a "error[E0499]: cannot borrow settings as mutable more than once at a time" at settings.save_settings() in main. I can understand this, because each call to a settings function has a self borrow and the self borrow for settings.pwm_from_setting() exists as long as the struct, i.e. until the end of the program.

If I remove the lifetime annotation and declare

      fn pwm_from_setting(& mut self); 

I get a "error: lifetime may not live long enough" for the "self.pwm" assignment.

Pwm<'a> has its own lifetime annotation and this seems to be the problem. But this is a type from another library and I cannot simply remove this. But why do I need to chain the Pwm lifetime to the self borrow lifetime. Isn't a struct field owned by the struct and shares the lifetime of the struct?

I could make Pwm static in my application but I am intrigued if there is a better solution.

Cheers,
Torsten

impl<'a> Type<'a> { fn f(&'a mut self … is essentially never what you actually want. Any &'a mut &'a … generally falls in the same bucket[1]. When you call PwmGuiData<'a>::pwm_from_setting, you're allowing the function to mutably borrow the data until 'a ends, thus locking yourself out of ever being able to touch the data again.

It smells like you're trying to create a single struct which has one field which borrows from another of its fields. This essentially is not something you're allowed to do; borrowing must be well structured as a directed graph.

But given the example construction of the Pwm doesn't capture 'a in any way, you probably want Pwm<'static>. Many libraries will use Ty<'a> as the borrowed type and Ty<'static> as the owning type (using something like Cow underneath), to avoid needing separate owning and nonowning types.


  1. The one exception: when both lifetimes are elided, then it has more correct applications. ↩︎

4 Likes

I'm basically saying the same thing as @CAD97 with different words in this comment.

Until the end of the validity of the struct anyway. But yes, that's the problem with &'a mut self; it exclusively borrows self "forever".


You haven't included the signature of function. But at a guess, it's trying to borrow from the self.current_setting value, and you're trying to assign that borrow back to self.pwm. If so, there's no safe way to do that without running into the borrowed-forever problem. (It's also extremely hard to do unsafely without being unsound.)

1 Like

Thank you. I understand the problem better now. The issue is with the library I am trying to use. The struct starts with

pub struct PasswordMaker<'a, T : HasherList>{
    username : &'a str,

It does not take ownership and I have to provide a long lived reference. So my problem is to tell the compiler that my String as the source for &str will be available long enough. It should be as both the string and Pwm<'a> are both in the same struct but &str can be quite tricky.

Cheers,
Torsten

You're may be confusing the liveness of the String value or of the containing struct (i.e. from creation to destruction) as a "lifetime". While common nomenclature in general, that's not what a Rust lifetime (one of those '_ things) is. They're more a (compile-time) tool to track borrows to avoid a lot of memory safety issues like aliased mutation (data races) and dangling references, etc.

Consider the following struct:

struct Example<'a> {
    pids: [u64; 100],
    current_pid: &'a u64,
}

If you made it self-referential and then moved the Example<'_>, the current_pid field would dangle because all the values in the pids field moved too. (Rust has no move constructors.)

It doesn't matter to this example that the pids haven't been destructed after the move.

There's no way for the borrow checker to differentiate this case from the String case, which is one reason (but not the only reason) why "straight-forward" self-referential structs aren't safely possible in a useful way.

Yes, there could be a data race issue.

So it is impossible to use the struct with the references as a field in my struct because i cannot provide a source for the reference with sufficient lifetime. So I could create the struct every time I need it and throw it away afterwards or change the &str references to String because sometimes you just have to make a copy.

Cheers,
Torsten

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.