Passing local lifetime to satisfy trait - rust

I have a generic function creating a local object and taking a trait specifying what to do with that object. The trait takes the reference to the object and holds it for it's lifetime (to avoid passing it again and again to every function call). It dies before the
fn do_stuff<'a, T>()
where T : BigBorrower<'a>
{
let borrowee = Borrowed{ data : 1 };
{
let _borrowee = T::new(&borrowee);
}
}
This is the function call. Because the lifetime for trait has to be specified in function declaraion, it makes the compiler think the lifetime extends lifetime of _borrowee.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a445fb4ab7befefbadd3bdb8fb43c86a
|
24 | fn do_stuff<'a, T>()
| -- lifetime `'a` defined here
...
29 | let _borrowee = T::new(&borrowee);
| -------^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `borrowee` is borrowed for `'a`
30 | }
31 | }
| - `borrowee` dropped here while still borrowed

You've just hit one of the issues with lifetimes and the compiler. Once you realize why it happens, it makes sense.
Your method call enforces a lifetime 'a for the generic type you're providing. This means, amongst other things, that this lifetime needs to be respected and that all objects are required to live for that long. In practice, when you are doing that, the lifetime is that of the function call.
By passing T::new() a reference to a local variable, you are forcing the compiler to pick a lifetime that is inferior to 'a (since it will not outlive the function call), and thus, you are going against your own requirements.
Typically, the way you solve this is to split your do_stuff<'a, T> into two, like on this playground sample. This makes the lifetime check palatable by the compiler, seeing as the life expectancy of that reference is guaranteed to be longer than that of the function being called.
Do note that I renamed your method new in the trait and implementations to borrow, as that's closer to what it is.

Related

A value that is no longer borrowed causes a "does not live long enough" error

This program cannot be compiled.
struct F<'a>(Box<dyn Fn() + 'a>);
fn main() {
let mut v = vec![]; // Vec<F>
let s = String::from("foo");
let f = F(Box::new(|| println!("{:?}", s)));
v.push(f);
drop(v);
}
error[E0597]: `s` does not live long enough
--> src/main.rs:7:44
|
7 | let f = F(Box::new(|| println!("{:?}", s)));
| -- ^ borrowed value does not live long enough
| |
| value captured here
...
11 | }
| -
| |
| `s` dropped here while still borrowed
| borrow might be used here, when `v` is dropped and runs the `Drop` code for type `Vec`
|
= note: values in a scope are dropped in the opposite order they are defined
When s is dropped(line 11), v is already dropped, so s is not borrowed.
But the compiler said that s was still borrowed. why?
This is due to the consideration that a panic could happen as a result of any function call, since there is no decoration on functions indicating whether they might panic.
When a panic occurs, the stack unwinds and the drop code for each (initialized) variable is run in the opposite of their declaration order (s is dropped first, and then v) But v has the type Vec<F<'a>> where 'a is the lifetime of s, and F implements Drop, which means that s cannot be dropped before v because the compiler can't guarantee that the drop code for F won't access s.
The compiler cannot tell that there isn't actually a memory safety issue here (if push panics, the vector doesn't reference s through the closure). All it knows is that the type of v must live at least as long as s; whether v actually contains a reference to s is immaterial.
To fix this, just swap the order v and s are declared in, which will guarantee that v is dropped before s.
But why does F implement Drop in the first place?
Note that the problem goes away if you remove the Fn() trait object and push the closure directly (e.g. without dyn). This case is different because the compiler knows that the closure doesn't implement Drop -- the closure didn't move-capture any values that implement Drop. Therefore, the compiler knows that s will not be accessed by v's drop code.
By comparison, trait objects always have a vtable slot for Drop::drop, and so the compiler must pessimistically assume that every trait object could have a Drop implementation. This means that when the Vec and Box are destroyed, the compiler emits code to call the trait object's drop code, and based on the information the compiler has, that can result in an access to s since the F value captures the lifetime of s.
This is one of the pitfalls about type erasure through trait objects: the trait object is opaque to the compiler and it can no longer verify that s won't be used by a Drop implementation of a boxed closure after s is dropped. If an owned trait object captures a lifetime, the compiler has to ensure that the captured lifetime does not end before the trait object is dropped.
The above is actually a somewhat simplified explanation. Rust's drop-checker is a bit more complex than this; it's okay if F auto-implements Drop so long as the drop-checker determines that the lifetime 'a doesn't get used. Because of the trait object, this can't be guaranteed. However, this code can compile with a Box holding a non-dyn closure as the drop-checker determines that the captured lifetime isn't used when dropping the box.

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 may a closure outlive the current function by borrowing a u32?

I'm getting
error[E0373]: closure may outlive the current function, but it borrows row_nr, which is owned by the current function
I would not expect this, since row_nr is a u32, so I'd expect it to be copied rather than moved:
fn get_neighbours(values: &Vec<Vec<u32>>, row: usize, col: usize) -> Vec<u32> {
vec![
values.get(row - 1).and_then(|cols| cols.get(col)),
values.get(row).and_then(|cols| cols.get(col - 1)),
values.get(row).and_then(|cols| cols.get(col + 1)),
values.get(row + 1).and_then(|cols| cols.get(col)),
].into_iter().filter_map(|value_opt| value_opt.map(|value| *value)).collect()
}
fn get_points(values: Vec<Vec<u32>>) -> Vec<(u32, Vec<u32>)> {
values
.iter()
.enumerate()
.flat_map(|(row_nr, columns)| {
columns.iter().enumerate().map(|(column_nr, height)| {
let neighbours = get_neighbours(&values, row_nr, column_nr);
(*height, neighbours)
})
}).collect()
}
Full error message:
--> src/lib.rs:16:44
|
16 | columns.iter().enumerate().map(|(column_nr, height)| {
| ^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `row_nr`
17 | let neighbours = get_neighbours(&values, row_nr, column_nr);
| ------ `row_nr` is borrowed here
|
note: closure is returned here
--> src/lib.rs:16:13
|
16 | / columns.iter().enumerate().map(|(column_nr, height)| {
17 | | let neighbours = get_neighbours(&values, row_nr, column_nr);
18 | | (*height, neighbours)
19 | | })
| |______________^
help: to force the closure to take ownership of `row_nr` (and any other referenced variables), use the `move` keyword
|
16 | columns.iter().enumerate().map(move |(column_nr, height)| {
| ++++
See also on the Rust playground.
Now, the error suggest using move, but that becomes problematic because I'd only like to pass a reference to the values Vec (which I don't think should outlive the closure, because get_neighbours takes u32s from that Vec and thus should make copies too, I think?).
I presume I'm misunderstanding lifetimes here. I'd appreciate any insights on what I'm misinterpreting. I looked at a couple of related questions, but they seem to either resolve it using a move (which, as mentioned above, I think I should (be able to) avoid?), or have multiple borrow checker issues, making it hard for me to understand which applies ones to me (e.g. this answer).
I would not expect this, since row_nr is a u32, so I'd expect it to be copied rather than moved
This expectation is correct, assuming it were moved in the first place. In this case it's not moved, it's borrowed, because by default closures borrow values from their environment. To request a move, which will for u32 indeed result in a copy, you need to use the move keyword explicitly.
As you discovered, when you just use move, you also move values, which doesn't compile and is anyway not what you want. The standard Rust idiom to move only one value into the closure is to use move and explicitly borrow the captured variables you don't want moved (in yout case values). For example, this compiles:
.flat_map(|(row_nr, columns)| {
columns.iter().enumerate().map({
let values = &values;
move |(column_nr, height)| {
let neighbours = get_neighbours(values, row_nr, column_nr);
(*height, neighbours)
}
})
})
Playground
As for your titular question of why may a closure outlive the current function: Note that the "current function" in the error message refers to the outer closure, the one passed to flat_map(). Since the inner closure becomes part of the iterator returned by map(), and the outer closure immediately returns that iterator, it is trivially true that the inner closure does outlive the "current function" and that it cannot be allowed to borrow either row_nr or columns. (But borrowing values is perfectly fine because values outlives both closures.)

How to read a lifetime error without looking at the code?

I'm getting the following lifetime error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> prusti-viper/src/procedures_table.rs:42:40
|
42 | let mut cfg = self.cfg_factory.new_cfg_method(
| ^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:5...
--> prusti-viper/src/procedures_table.rs:40:5
|
40 | / pub fn set_used(&mut self, proc_def_id: ProcedureDefId) {
41 | | let procedure = self.env.get_procedure(proc_def_id);
42 | | let mut cfg = self.cfg_factory.new_cfg_method(
43 | | // method name
... |
135| | self.procedures.insert(proc_def_id, method);
136| | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> prusti-viper/src/procedures_table.rs:42:23
|
42 | let mut cfg = self.cfg_factory.new_cfg_method(
| ^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'v as defined on the impl at 22:1...
--> prusti-viper/src/procedures_table.rs:22:1
|
22 | impl<'v, P: Procedure> ProceduresTable<'v, P> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...so that the expression is assignable:
expected viper::Method<'v>
found viper::Method<'_>
error: aborting due to previous error
Without looking at the code, just by reading the error message, is it possible to understand to which lifetimes/references/borrows is the error message referring to? Here is the message annotated with my questions:
error[E0495]: cannot infer an appropriate lifetime for autoref (what is autoref?) due to conflicting requirements
note: first, the lifetime (which lifetime?) cannot outlive the anonymous lifetime #1 (the one of &mut self, ok) defined on the method body at 40:5...
...so that reference (which reference?) does not outlive borrowed content (which borrowed content?)
but, the lifetime must be valid for the lifetime 'v as defined on the impl at 22:1... (why these constraints?)
For example, I'm looking for an explanation like "In error message E0495 the lifetime that cannot outlive the anonymous lifetime #1 is always the lifetime of self, in other words #1 again".
By looking at existing answers to similar questions (https://stackoverflow.com/a/35519236/2491528, https://stackoverflow.com/a/30869059/2491528, https://stackoverflow.com/a/41271422/2491528), I can't find an explanation of what the error message is referring to. Sometimes the answer just writes "in this case the lifetime is 'a", but I'm wondering how to understand that it's 'a and not some other 'b. Other times the answer involves reasoning on the source code, but that would be one of the following steps for me: first of all read the message and understand what it's referring to, then understand the error (in this case, probably conflicting lifetime requirements), then look at the code and try to fix the error.
cannot infer an appropriate lifetime for autoref due to conflicting requirements
This is the key part of the error. There are two (or more) requirements on a lifetime, and they conflict. The "autoref" means the reference that's taken by calling a method that takes &self. The quoted line of code indicates which method call is erroneous.
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:5
This is the first conflicting requirement. "The lifetime" means the one referred to in the first message: the one it's trying to infer, for this autoref. It can't outlive the object you're calling the method on. The quoted code indicates the lifetime of that object. In this case, the lifetime is the whole method that the erroneous line is in, because the object you're borrowing is a member of that method's &mut self.
note: ...so that reference does not outlive borrowed content
This just explains that a bit further. "That reference" - the autoref you're trying to take - can't outlive the object that &mut self refers to.
note: but, the lifetime must be valid for the lifetime 'v as defined on the impl at 22:1...
The "but" here introduces the second requirement, which conflicts with the first requirement.
You ask "why these constraints?", and the compiler explains right away:
note: ...so that the expression is assignable:
expected viper::Method<'v>
found viper::Method<'_>
The assignment in question is the one in the erroneous line. You're assigning the result of new_cfg_method to cfg. The "expected" is the left hand-side of the assignment, cfg, which must be of type viper::Method<'v>. The "found" is the right-hand side, the result of the method call, which has type viper::Method<'_>. '_ means the lifetime the compiler is trying to infer. That is, it's the way you subsequently use cfg that means it must have the lifetime 'v. Why that is depends on the code that's not quoted in the error message.
To fix this, you need to do one of the following:
Cut out the first requirement. Change new_cfg_method so that the lifetime of its result is not tied to the lifetime of the object you call it on: perhaps by removing some references it contains.
Cut out the second requirement. Change the code that uses cfg so it doesn't need to have the lifetime 'v. Again, this might be achieved by removing some references in viper::Method.
If you can't do either, you might need to introduce Cell or something else to manage lifetimes dynamically instead of statically.

When would an implementation want to take ownership of self in Rust?

I'm reading through the Rust documentation on lifetimes. I tried something like:
struct S {
x: i8,
}
impl S {
fn fun(self) {}
fn print(&self) {
println!("{}", self.x);
}
}
fn main() {
let s = S { x: 1 };
s.fun();
s.print();
}
I get the following error:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:16:5
|
15 | s.fun();
| - value moved here
16 | s.print();
| ^ value borrowed here after move
|
= note: move occurs because `s` has type `S`, which does not implement the `Copy` trait
This is because the fun(self) method takes ownership of the s instance. This is solved by changing to fun(&self).
I can't see why you would ever want to have a method on an object take control of itself. I can think of only one example, a destructor method, but if you wanted to do dispose of the object then it would be taken care of by the owner of the object anyway (i.e. scope of main in this example).
Why is it possible to write a method that takes ownership of the struct? Is there ever any circumstance where you would want this?
The idiomatic way to refer to a method that "takes control" of self in the Rust standard library documentation is to say that it "consumes" it. If you search for this, you should find some examples:
Option::unwrap_or_default
A lot in the Iterator trait.
As to why: you can try rewriting Iterator::map — you would end up having a lifetime parameter wandering around that would quickly become unmanageable. Why? Because the Map iterator is based upon the previous one, so the borrow checker will enforce that you can only use one of the two at the same time.
Conversion from type A to type B commonly involves functions taking self by value. See the implementors of Into and From traits for concrete examples.

Resources