2

I'm having a hard time resolving a lifetime issue:

pub struct A<'a> {
    pub a: &'a str,
    pub b: &'a u8,
}

pub enum Executable<'a> {
    ExecutableA(A<'a>),
}

pub struct Config {
    pub a: String,
    pub b: u8, // some more config values to follow
}

impl Config {
    pub fn new() -> Config {
        // Implementation details omitted due to irrelevancy
        unimplemented!();
    }
}

pub struct Cli<'a> {
    pub config: Config,
    pub execution_list: Vec<Executable<'a>>,
}

impl<'a> Cli<'a> {
    pub fn new() -> Cli<'a> {
        Cli {
            config: Config::new(),
            execution_list: vec![],
        }
    }

    pub fn prepare(&mut self) {
        self.execution_list.push(
            // Compilation error occurs for following line
            Executable::ExecutableA(A {
                a: &self.config.a,
                b: &self.config.b,
            }),
        )
    }
}

The compilation error is:

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/lib.rs:39:20
   |
39 |                 a: &self.config.a,
   |                    ^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 35:20...
  --> src/lib.rs:35:20
   |
35 |     pub fn prepare(&mut self) {
   |                    ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:39:20
   |
39 |                 a: &self.config.a,
   |                    ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 27:6...
  --> src/lib.rs:27:6
   |
27 | impl<'a> Cli<'a> {
   |      ^^
note: ...so that the expression is assignable
  --> src/lib.rs:38:13
   |
38 | /             Executable::ExecutableA(A {
39 | |                 a: &self.config.a,
40 | |                 b: &self.config.b,
41 | |             }),
   | |______________^
   = note: expected `Executable<'a>`
              found `Executable<'_>`

(Playground)

After doing a lot of reading and looking at other questions, I still can't convince myself that I'm understanding the error correctly, mainly because I can't reason why the compiler is attaching the "anonymous" lifetime to the &mut self reference in the prepare function.

My general design idea is that I'll have my Cli struct contain a config, and a list of executables such that I can add all relevant executables to that list in the Cli's prepare function (where I'd like those executables to be able to reference values owned by the Config). I'll then iterate over that execution list to start/stop executables.

I think one answer to this question is to not have executables maintain references to config values and instead copy those values, but I feel like that shouldn't be necessary, and would like to use this issue as a learning opportunity.

I'm totally open to suggestions around "Maybe you should rethink your design and do X, instead."

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
bd8080
  • 29
  • 2
  • 1
    This is just a self-referential struct, but disguised because the references and the values they reference are inside nested structs. – Sebastian Redl Nov 29 '21 at 21:20
  • Yep, I thought it was a self-referential struct as well. With that being said, what's a "Rusty" way to approach this, then? – bd8080 Nov 29 '21 at 21:22
  • @SebastianRedl I had read over that answer a handful of times before posting this question - I don't think it necessarily clarifies anything around this compilation error but there's definitely some smelly code here. – bd8080 Nov 29 '21 at 21:29
  • Naturally, an object has to live at least as long as any references to data owned by it. If `&'a must self` isn't viable because it requires too long a borrow, you can try using `Rc` instead of `String`/`&'a str` in `Config`'s/`A`'s members. – BallpointBen Nov 30 '21 at 05:49

1 Answers1

-1

Anonymous lifetimes are inferred by the compiler, most of them will point to 'static,

If you add this (&'a mut self):

pub fn prepare (&'a mut self) /* ... */ 

It compiles just fine, since the impl lifetime requirement is not conflicting with the Executable one.

Edit:

As pointed out, i was wrong (probably because the rust style did reach into my mind but lifetimes did not), i though about generic lifetimes but i looked at the already existing lifetime 'a and found that it compiled, at least mutably borrowing the class for eternity makes sense.

About your design:

I found it good for what you are trying to do, also i am not the expert with designs, specially in rust, so i found nothing wrong about it.

Viable solution:

Like pointed out, you could add the generic lifetime:

pub fn prepare <'b> (&'b mut self) { ... }

Now whenever the function is called the mutable borrow will last the needed lifetime, and when you want to immutably borrow it, it will work.

  • 3
    That compiles just fine, but there's a major problem in obtaining a mutable reference to `self` > If we have some struct generic over 'a we almost never want to write a method with a &'a mut self receiver. What we're communicating to Rust is "this method will mutably borrow the struct for the entirety of the struct's lifetime". In practice this means Rust's borrow checker will only allow at most one call to some_method before the struct becomes permanently mutably borrowed and thus unusable. – bd8080 Nov 29 '21 at 23:35
  • https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#5-if-it-compiles-then-my-lifetime-annotations-are-correct – bd8080 Nov 29 '21 at 23:36
  • Thank ya, edited it based on what you pointed out, not really useful but it is there if you need – Rafaelplayerxd YT Nov 29 '21 at 23:55
  • This answer was my first thought as well, so I'm glad it's documented that, and why, it doesn't work. – BallpointBen Nov 30 '21 at 05:52