Issue with lifetime of borrowed self

I have a problem to solve where I need a Data struct with a reference to it's Builder in order to retain some useful properties. I then also have a Container struct which holds a vector of Data structs as well as a reference to the Builder. The container struct has a fill method to push instances of Data to the vector after building one using some math; I have the build_one method for this purpose. When I try to use the build_one method in fill, I get issues about the lifetime of self, since &self is used by build_one, then &mut self is required to push to the vector, but the &self for build_one is still alive for some reason when that occurs. If I work around the issue by extracting the self.builder.build and running that on the calculated value, everything works, so logically the compiler is able to resolve the lifetimes as intended.

Why is this happening? Is there a way I can specify what the lifetimes should be so that I don't need the extra self.builder.build line to rebuild the same object twice?

#[derive(Debug)]
struct Data<'a> {
    value: i32,
    builder: &'a Builder,
}

impl<'a> Data<'a> {
    fn get_original(&self) -> i32 {
        self.value / self.builder.multiplier
    }
}

#[derive(Debug)]
struct Builder {
    multiplier: i32,
}

impl Builder {
    fn build(&self, value: i32) -> Data {
        Data {
            value: self.multiplier * value,
            builder: self,
        }
    }
}

#[derive(Debug)]
struct Container<'a> {
    vals: Vec<Data<'a>>,
    builder: &'a Builder,
}

impl<'a> Container<'a> {
    fn build_one(&self, value: i32) -> Data {
        // Do math with value
        self.builder.build(value)
    }

    fn fill(mut self, value: i32) -> Self {
        // This line causes errors
        let new = self.build_one(value);
        // But this one is okay
        // let new = self.builder.build(value);
        // Or adding this lets it compile
        // let new = self.builder.build(new.value);

        self.vals.push(new);
        self
    }
}

fn main() {
    let builder = Builder { multiplier: 7 };
    let container = Container {
        vals: vec![],
        builder: &builder,
    }
    .fill(1)
    .fill(2);

    println!("{:?}", container);
    for d in container.vals {
        println!(
            "{} * {} = {}",
            d.builder.multiplier,
            d.get_original(),
            d.value
        );
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0597]: `self` does not live long enough
  --> src/main.rs:41:19
   |
33 | impl<'a> Container<'a> {
   |      -- lifetime `'a` defined here
...
39 |     fn fill(mut self, value: i32) -> Self {
   |             -------- binding `self` declared here
40 |         // This line causes errors
41 |         let new = self.build_one(value);
   |                   ^^^^^^^^^^^^^^^^^^^^^
   |                   |
   |                   borrowed value does not live long enough
   |                   argument requires that `self` is borrowed for `'a`
...
49 |     }
   |     - `self` dropped here while still borrowed

error[E0502]: cannot borrow `self.vals` as mutable because it is also borrowed as immutable
  --> src/main.rs:47:9
   |
33 | impl<'a> Container<'a> {
   |      -- lifetime `'a` defined here
...
41 |         let new = self.build_one(value);
   |                   ---------------------
   |                   |
   |                   immutable borrow occurs here
   |                   argument requires that `self` is borrowed for `'a`
...
47 |         self.vals.push(new);
   |         ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

error[E0505]: cannot move out of `self` because it is borrowed
  --> src/main.rs:48:9
   |
33 | impl<'a> Container<'a> {
   |      -- lifetime `'a` defined here
...
39 |     fn fill(mut self, value: i32) -> Self {
   |             -------- binding `self` declared here
40 |         // This line causes errors
41 |         let new = self.build_one(value);
   |                   ---------------------
   |                   |
   |                   borrow of `self` occurs here
   |                   argument requires that `self` is borrowed for `'a`
...
48 |         self
   |         ^^^^ move out of `self` occurs here

Some errors have detailed explanations: E0502, E0505, E0597.
For more information about an error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to 3 previous errors

This is a lifetime elision issue. Your build_one impl has a signature of

fn build_one(&self, value: i32) -> Data

which is equivalent to

fn build_one<'s>(&'a self, value: i32) -> Data<'s>

In other words, the method says it will return a Data that lives as long as the borrow of self it is called with. If you explicitly use the lifetime on Container it works

fn build_one(&self, value: i32) -> Data<'a>
4 Likes

When you put:

impl<'a> Container<'a> {
    fn build_one(&self, value: i32) -> Data {
        // Do math with value
        self.builder.build(value)
    }
}

it is equivalent to:

impl<'a> Container<'a> {
    fn build_one<'s>(&'s self, value: i32) -> Data<'s> {
        // Do math with value
        self.builder.build(value)
    }
}

But this is not an accurate lifetime for Data. What you want is:

impl<'a> Container<'a> {
    fn build_one(&self, value: i32) -> Data<'a> {
        // Do math with value
        self.builder.build(value)
    }
}

This compiles.

3 Likes

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.