Lifetime issue for a borrowed value inside an async trait - rust

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?

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.

Related

solving "argument requires that `x` is borrowed for `'y`"

tl;dr blocked by "argument requires that `x` is borrowed for `'y`"; how can I coerce variable x to lifetime 'y?
Given the following code, I'm blocked when trying to create the Box pointer pointing to a reference. I know the referenced object instance lives long enough. However, the rust compiler is concerned it does not.
How do I tell the rust compiler that Box::new(&thing) is valid for the lifetime of the containing struct instance?
Code example
This code is essentially:
Things (a Vec), holding multiple Things
counter of Things (a HashMap), holding multiple Box pointers to different &Thing as keys, a u64 count as values
(My best attempt at) a minimal example (rust playground):
use std::collections::HashMap;
type Thing<'a> = (&'a str, usize);
type Things<'a> = Vec<Thing<'a>>;
type ThingsCounterKey<'a> = Box<&'a Thing<'a>>;
type ThingsCounter<'a> = HashMap<ThingsCounterKey<'a>, u64>;
pub struct Struct1<'struct1> {
things1: Things<'struct1>,
thingscounter1: ThingsCounter<'struct1>,
}
impl<'struct1> Struct1<'struct1> {
pub fn new() -> Struct1<'struct1> {
Struct1{
things1: Things::new(),
thingscounter1: ThingsCounter::new(),
}
}
fn things_update(&mut self, thing_: Thing<'struct1>) {
self.things1.push(thing_);
let counter = self.thingscounter1.entry(
ThingsCounterKey::new(&thing_)
).or_insert(0);
*counter += 1;
}
}
fn main() {
let mut s1 = Struct1::new();
for (x, y) in [("a", 1 as usize), ("b", 2 as usize)] {
s1.things_update((x, y));
}
}
This results in compiler error:
error[E0597]: `thing_` does not live long enough
--> src/main.rs:24:35
|
14 | impl<'struct1> Struct1<'struct1> {
| -------- lifetime `'struct1` defined here
...
24 | ThingsCounterKey::new(&thing_)
| ----------------------^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `thing_` is borrowed for `'struct1`
...
27 | }
| - `thing_` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
How can I tell the rust compiler "&thing_ has the lifetime of 'struct1"?
Similar questions
Before this Question is marked as duplicate, these questions are similar but not quite the same. These Questions address 'static lifetimes or other slightly different scenarios.
Argument requires that value is borrowed for 'static not working for non copy value
Argument requires that _ is borrowed for 'static - how do I work round this?
"argument requires that record is borrowed for 'static" when using belonging_to associations with tokio-diesel
(tokio::spawn) borrowed value does not live long enough -- argument requires that sleepy is borrowed for 'static
Rust lifetime syntax when borrowing variables
Borrow data out of a mutex "borrowed value does not live long enough"
Rust: argument requires that borrow lasts for 'static
thing_ is passed by value. Its lifetime ends at the end of things_update. That means, the reference to thing_ becomes invalid after the end of the function but still be held by the struct, which is certainly should be rejected.
Like many times, actually the compiler is right and you were wrong. The compiler prevented you from use-after-free.
thing_ doesn't live long enough: you pushed a copy of it into the vector. It was clear if you'd used a type that isn't Copy:
type Thing<'a> = (&'a str, String);
// ...
Besides the previous error, now you also get:
error[E0382]: borrow of moved value: `thing_`
--> src/main.rs:24:35
|
21 | fn things_update(&mut self, thing_: Thing<'struct1>) {
| ------ move occurs because `thing_` has type `(&str, String)`, which does not implement the `Copy` trait
22 | self.things1.push(thing_);
| ------ value moved here
23 | let counter = self.thingscounter1.entry(
24 | ThingsCounterKey::new(&thing_)
| ^^^^^^^ value borrowed here after move
Playground.
In other case, you would have to first push the item into the vector and then retrieve a reference to the pushed item there, like:
self.things1.push(thing_);
let counter = self.thingscounter1.entry(
ThingsCounterKey::new(self.things1.last().unwrap())
).or_insert(0);
But in this case it will not work, and you'll get a long "cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements" error. This is because you're essentially trying to do the impossible: create a self-referential struct. See Why can't I store a value and a reference to that value in the same struct? for more about this problem and how to solve it.

Why can't the Rust borrow checker take advantage of `'static` bounds?

Consider the following Rust program:
#![feature(generic_associated_types)]
pub trait Func {
type Input<'a>;
type Output;
fn call(self, input: Self::Input<'_>) -> Self::Output;
}
fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
where F: 'static + Func<Input<'cx> = &'cx u8, Output = u8>,
{
let input = &*ctx;
let out = f.call(input);
*ctx = out;
}
I've used #![feature(generic_associated_types)], but I think the question I'm asking is still relevant if you move 'a from Func::Input to Func and use a higher-rank trait bound on invoke.
This code errors, but I don't think it's unsound:
error[E0506]: cannot assign to `*ctx` because it is borrowed
--> src/lib.rs:15:5
|
10 | fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
| --- lifetime `'cx` defined here
...
13 | let input = &*ctx;
| ----- borrow of `*ctx` occurs here
14 | let out = f.call(input);
| ------------- argument requires that `*ctx` is borrowed for `'cx`
15 | *ctx = out;
| ^^^^^^^^^^ assignment to borrowed `*ctx` occurs here
First ctx is reborrowed as input, which is passed to f.call and then never used again. f.call returns a value that does not contain any lifetimes (u8: 'static), so there is no connection between out and ctx.
Likewise, the type of f contains no lifetimes (F: 'static), so it cannot hold a reference with lifetime 'cx. Furthermore, the lifetime 'cx cannot be safely coerced to 'static inside call, so there's no way to "smuggle out" a reference with that lifetime that's accessible beyond the invocation of f.call. Therefore, I don't see how anything can alias ctx, and I think assigning to it in the last line should be sound.
Am I missing something? Would accepting this code be unsound? If not, why does Rust fail to take advantage of 'static bounds in this way?
The lifetime 'cx could be 'static meaning input can be smuggled elsewhere and be invalidated by *ctx = out.
There's no way to constrain that a lifetime is strictly less than another, so I don't think adding a "broader" lifetime constraint to a generic type is even considered by the borrow checker.
The code as written is unsound. The lifetime bound of 'static on F is completely irrelevant, because the lifetime bounds of F::Input and F are two distinct lifetimes, and it's the associated type's lifetime that's causing the error. By declaring F::Input<'ctx> = &'ctx u8, you are declaring that the immutable borrow lives the length of the mutable one, making the mutable reference unsafe to use.
As #Stargateur mentioned, the thing that can make this work are Higher Ranked Trait bounds:
fn invoke<F>(f: F, ctx: &mut u8)
where F: for<'ctx> Func<Input<'ctx> = &'ctx u8, Output = u8>,
{
let input = ctx;
let out = f.call(input);
*input = out;
}
Playground
That is, instead of declaring that the function call is valid for some specific lifetime 'ctx it is valid for all lifetime's 'ctx. This way, the compiler can freely pick an appropriate lifetime for the reborrow to make this work.
As a side note, you might think that using two specific lifetimes in the function definition would be able to work, but any attempt to do so results in the compiler failing to choose the appropriate lifetime that makes things work.

Why does the compiler error complain about multiple mutable references not dangling reference?

I am trying to understand what exactly happens when functions reborrow mutable references.
fn main() {
let mut a = String::new();
let mut b = String::new();
let aa = &mut a;
let c = my_fun(aa, &mut b);
let d = &mut a;
println!("{}", c);
}
fn my_fun<'b>(x: &'b mut String, y: &'b mut String) -> &'b mut String { y }
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun. But due to the lifetime bound I created in the function signature the reborrow should live at least as long as &mut b exist. So the println force the reborrow to live until it.
Shouldn't this be throwing an error of use after free because the anonymous reborrow has only scope inside my_fun? Outside of this function this reference shouldn't be valid.
But the error I get is:
error[E0499]: cannot borrow `a` as mutable more than once at a time
--> src/main.rs:7:13
|
5 | let aa= &mut a;
| ------ first mutable borrow occurs here
6 | let c = my_fun(aa, &mut b);
7 | let d = &mut a;
| ^^^^^^ second mutable borrow occurs here
8 | println!("{}", c);
| - first borrow later used
which would have made sense if the mutable reference was merely copied instead of reborrowed inside the function.
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun.
It's not quite that.
Let's backtrack a bit: why reborrowing?
There is a fundamental difference between &T and &mut T: &T is Copy, whereas &mut T is not even Clone. The result is that &mut T can only be moved and therefore a call such as:
let x: &mut T = /*...*/;
func(x);
Would result in x being unusable after the call. The work-around would then be to introduce a temporary:
let x: &mut T = /*...*/;
let tmp = &mut *x;
func(tmp);
This temporary would re-borrow the pointee of x, and be consumed by func.
And... that's re-borrowing! The compiler has this "temporary" creation built-in purely for ergonomics!
With that in mind, let's go back to:
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun.
Lifetimes are generally more a range than a point, and this is true here.
The lifetime of tmp in my example above is constrained in 2 ways:
It cannot be greater than that of x.
It cannot be less than that of func.
Which is another way of saying that it can be anything in between those bounds.
I believe you're overthinking "reborrowing" here.
The lifetime requirements you applied say that the thing being referenced by the return value will have at least the lifetime of the things being referenced by the parameters. That's true (and if it weren't provably true, this wouldn't compile). So there is no dangling reference possible.
There isn't a separate "reborrowed" reference. Borrowing is bookkeeping inside the compiler to keep track of lifetimes. There is no let x = &*aa step that actually occurs or is even particularly implied. This isn't like reference counting where memory actually changes at runtime.
Inside of my_fun, y is a reference that's scoped to the function. But the return value is scoped to the caller. (Functions would be impossible to use if this weren't true, having nothing to do with &mut.)

Where does the static requirement come from?

I have a very small repo which currently does not compile, with the following error:
error[E0597]: `line` does not live long enough
--> src/main.rs:19:20
|
19 | let line = line.trim();
| ^^^^-------
| |
| borrowed value does not live long enough
| argument requires that `line` is borrowed for `'static`
...
22 | }
| - `line` dropped here while still borrowed
I've had trouble producing a minimal example demoing the problem: this (playground) works as expected, despite that fn main is identical to the one which fails, as are the signatures of ItemParser::parse and Item::pretty_to.
Inlining a section of fn main, and some signatures:
let parser = ItemParser::new();
let stdin = stdin();
let reader = stdin.lock();
let reader = BufReader::new(reader);
let stdout = stdout();
let mut writer = stdout.lock();
for line in reader.lines() {
let line = line?;
let line = line.trim();
let item = parser.parse(line)?;
item.pretty_to(&mut writer)?;
}
The same issue persists when I comment out item.pretty_to(&mut writer)?;, so I believe that that isn't the problem.
I can't show the actual code for ItemParser as it's generated by LALRPOP, but the signature as reported by rustdoc is
pub struct ItemParser { /* fields omitted */ }
pub fn parse<'input>(
&self,
input: &'input str
) -> Result<Item<'input>, ParseError<usize, Token<'input>, &'static str>>
Unlike this issue, nothing in this crate explicitly requires a 'static lifetime.
My expectation is that at the head of the for loop, item has type io::Result<String>. After we discard the error and trim the edges, it should have type &'a str, where 'a is the lifetime scoped to this iteration of the for loop. In that case, parsing the line should produce an Item<'a> with the same lifetime. It drops before the lifetime ends, in appropriate sequence. As nothing visibly requests a 'static lifetime, I don't know where that requirement is coming from.
On error, parser.parse() yields a type that is bounded to the lifetime of the input.
Result<Item<'input>, ParseError<usize, Token<'input>, &'static str>>
// ^^^^^^^^^^^^^
You're using ? to return the error from main (not "discard" it), which by necessity will outlive the loop, and therefore line.
You can handle the error immediately via match or if let, or do something like parser.parse().map_err(...)? to transform the error into something not bounded to the lifetime of line.
Answering the title specifically, The 'static requirement is from using eyre::Result which is an alias for Result<T, E = Report>. Report can be created from any Error type (which ParseError is), but is constrained to be 'static. It is a shame the compiler doesn't bring this up though.
Since str is a sort of slice, it can not outlive its parent. The result on trim is really just a slice the original string.
pub fn trim(&self) -> &str
// Now with the implicit lifetimes
pub fn trim<'a, 'b: 'a>(&'b self) -> &'a str
Since none of the lifetimes of the function arguments would not make appropriate bounds for the lifetime of the function output, it must have a different lifetime and the only appropriate lifetime for an unconstrained reference is 'static.
Its basically the equivalent to returning a reference to data you just freed in c.
Foo *data_to_return = &foo->bar;
free(foo);
return data_to_return;
But it's kinda a moot point anyway because the function would never work in the first place. The lifetime is more of a symptom of your issue than the cause. Just ignore the lifetime for now since it sounds like its holding you up from actually fixing the issue. Once you fix your code, the 'static bound will go away.

Is this error due to the compiler's special knowledge about RefCell?

fn works<'a>(foo: &Option<&'a mut String>, s: &'a mut String) {}
fn error<'a>(foo: &RefCell<Option<&'a mut String>>, s: &'a mut String) {}
let mut s = "hi".to_string();
let foo = None;
works(&foo, &mut s);
// with this, it errors
// let bar = RefCell::new(None);
// error(&bar, &mut s);
s.len();
If I put in the two lines with the comment, the following error occurs:
error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
--> <anon>:16:5
|
14 | error(&bar, &mut s);
| - mutable borrow occurs here
15 |
16 | s.len();
| ^ immutable borrow occurs here
17 | }
| - mutable borrow ends here
The signatures of works() and errors() look fairly similar. But apparently the compiler knows that you can cheat on it with a RefCell, because the borrow checker behaves differently.
I can even "hide" the RefCell in another type of my own, but the compiler still always does the right thing (errors in case a RefCell could be used). How does the compiler know all that stuff and how does it work? Does the compiler mark types as "interior mutability container" or something like that?
RefCell<T> contains an UnsafeCell<T> which is a special lang item. It is UnsafeCell that causes the error. You could check with:
fn error<'a>(foo: &UnsafeCell<Option<&'a mut String>>, s: &'a mut String) {}
...
let bar = UnsafeCell::new(None);
error(&bar, &mut s);
But the error is not due to compiler recognizing an UnsafeCell introduces interior mutability, but that an UnsafeCell is invariant in T. In fact, we could reproduce the error using PhantomData:
struct Contravariant<T>(PhantomData<fn(T)>);
fn error<'a>(foo: Contravariant<&'a i32>, s: &'a mut String) {}
...
let bar = Contravariant(PhantomData);
error(bar, &mut s);
or even just anything that is contravariant or invariant in the lifetime 'a:
fn error<'a>(foo: Option<fn(&'a i32)>, s: &'a mut String) {}
let bar = None;
error(bar, &mut s);
The reason you can't hide a RefCell is because variance is derived through the fields of the structure. Once you used RefCell<T> somewhere, no matter how deep, the compiler will figure out T is invariant.
Now let's see how the compiler determine the E0502 error. First, it's important to remember that the compiler has to choose two specific lifetimes here: the lifetime in the type of the expression &mut s ('a) and the lifetime in the type of bar (let's call it 'x). Both are restricted: the former lifetime 'a has to be shorter than the scope of s, otherwise we would end up with a reference living longer than the original string. 'x has to be larger than the scope of bar, otherwise we could access an dangling pointer through bar (if a type has a lifetime parameter the compiler assume the type can access a value with that lifetime).
With these two basic restriction, the compiler goes through the following steps:
The type bar is Contravariant<&'x i32>.
The error function accepts any subtype of Contravariant<&'a i32>, where 'a is the lifetime of that &mut s expression.
Thus bar should be a subtype of Contravariant<&'a i32>
Contravariant<T> is contravariant over T, i.e. if U <: T, then Contravariant<T> <: Contravariant<U>.
So the subtyping relation can be satisfied when &'x i32 is a supertype of &'a i32.
Thus 'x should be shorter than 'a, i.e. 'a should outlive 'x.
Similarly, for an invariant type, the derived relation is 'a == 'x, and for convariant, 'x outlives 'a.
Now, the problem here is that the lifetime in the type of bar lives until the end of scope (as per restriction mentioned above):
let bar = Contravariant(PhantomData); // <--- 'x starts here -----+
error(bar, // |
&mut s); // <- 'a starts here ---+ |
s.len(); // | |
// <--- 'x ends here¹ --+---+
// |
// <--- 'a ends here² --+
}
// ¹ when `bar` goes out of scope
// ² 'a has to outlive 'x
In both contravariant and invariant cases, 'a outlives (or equals to) 'x means the statement s.len() must be included in the range, causing borrowck error.
Only in the covariant case we could make the range of 'a shorter than 'x, allowing the temporary object &mut s be dropped before s.len() is called (meaning: at s.len(), s is not considered borrowed anymore):
let bar = Covariant(PhantomData); // <--- 'x starts here -----+
// |
error(bar, // |
&mut s); // <- 'a starts here --+ |
// | |
// <- 'a ends here ----+ |
s.len(); // |
} // <--- 'x ends here -------+

Resources