1

I have this method

async fn insert<'a>(&mut self, params: &'a[&'a dyn QueryParameters<'a>]) {
    let mut mapped_fields: String = String::new();
    let values: &'a[&'a QueryParameters<'a>] = &[#(#insert_values),*];
    // #insert_transaction
}

I would like to know why the compiler and the borrow checker complains about this:

^^^^^^^^^-
  |                        |        |
  |                        |        temporary value is freed at the end of this statement
  |                        creates a temporary which is freed while still in use
  |                        lifetime `'a` defined here
  |                        type annotation requires that borrow lasts for `'a`

If I just remove the 'a lifetime annotation from the explicit type declaration, everything works. This piece of code is inside a derive proc-macro that it's marked as async-trait, so values is getting 'life0 (if I do not explicitly declare it's lifetime bound as 'a), which is the same as the &mut self.

But, I am confused. I make a new variable (values), as a slice array, but... that variable doesn't goes anywhere. I mean. I am just creating the slice on the let values assignment, but doing anything with it. So, Drop should come at the end of the method and cleaning up the variables for the current stack frame. Fine. But, I am doing nothing with that slice.

Can someone help me to understand better the borrow checker?

Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
  • 2
    `&'a QueryParameters<'a>` is almost always wrong (and the same goes for `&'a[&'a …]`, it probably should be `&'a[&'b QueryParameter<'c>]` – Jmb Sep 30 '22 at 10:12
  • @Jmb but... why? – Alex Vergara Sep 30 '22 at 10:24
  • @Jmb Let me emphasize the [_almost_](https://doc.rust-lang.org/stable/std/thread/struct.Builder.html#method.spawn_scoped) part in your comment. – rodrigo Sep 30 '22 at 19:53
  • 1
    `&'a dyn Trait<'a>` makes the reference overly constrained since its tied to the second use which is invariant and means the compiler does not have the same flexibility it usually does when determining the lifetime. You can run into issues like this: [Why does this mutable borrow live beyond its scope?](/q/66252831/2189130) – kmdreko Sep 30 '22 at 22:01

1 Answers1

0

You can get a similar error with a simpler code:

fn test<'a>(x: &'a str) {
    let p: &'a str = &String::new();
}

That results in:

error[E0716]: temporary value dropped while borrowed
 --> src/lib.rs:2:23
  |
1 | fn test<'a>(x: &'a str) {
  |         -- lifetime `'a` defined here
2 |     let p: &'a str = &String::new();
  |            -------    ^^^^^^^^^^^^^ creates a temporary which is freed while still in use
  |            |
  |            type annotation requires that borrow lasts for `'a`
3 | }
  | - temporary value is freed at the end of this statement

The cause is that the 'a lifetime is a generic argument to the function, that is decided by the caller of that function, and that lifetime will certainly contain the whole execution of the function. For example if calling it with:

let z = String::from("foo");
test(&z);

Then 'a will be the lifetime of that z argument.

Now, when you use that lifetime to annotate a local value such as:

let p: &'a str = &String::new();

or equivalently:

let temp = String::new();
let p: &'a str = &temp;

you are requiring that this reference lives at least as long as 'a. But that is certainly impossible, because temp is a local value, it will never outlive the function itself.

This is precisely what the error is trying to say: you define a lifetime 'a as a generic argument, then you borrow a value that is required to live for 'a because of a type annotation, but that value is dropped sooner than it should be.

Naturally, if you use the argument of the function such as in:

let p: &'a str = &*x;

then all goes well, because *x is explicitly declared to live for 'a.

On the other hand, if you omit the lifetime annotation:

let p: &str = &temp;

then the compiler will just guess the lifetime of temp as a local lifetime and assign that to this reference. And all will go well, too.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Perfect. Just `let p: &'a i32 = &*x;` where it should be `let p: &'a str = &*x; ` – Alex Vergara Sep 30 '22 at 17:25
  • @AlexVergara Fixed, thanks. My first version used `&i32` instead of `&str` but `&42` is actually `'static`, not a local temporary value, so it didn't fit your original code too well. – rodrigo Sep 30 '22 at 19:48