How to bind reference lifetime to a function local scope - rust

I want to write a function A which takes as parameter a function B which takes as parameter a type which is parameterized by a reference type which lifetime is at least the lifetime of the local variables in A’s body.
Consider the following example:
struct Foo {}
fn consume(mut v: Vec<&Foo>) {
while let Some(..) = v.pop() {
// Do stuff
continue;
}
}
fn setup_and<F>(f: F)
where
F: FnOnce(&mut Vec<&Foo>) + Send,
{
let mut v: Vec<&Foo> = vec![];
let other_foo = Foo {};
f(&mut v);
v.push(&other_foo);
consume(v);
}
fn main() {
let foo = Foo {};
setup_and(|v| {
v.push(&foo);
});
}
rustc cannot infer lifetimes on its own. It complains:
error[E0597]: `foo` does not live long enough
--> src/main.rs:25:17
|
24 | setup_and(|v| {
| --- value captured here
25 | v.push(&foo);
| --------^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `foo` is borrowed for `'static`
26 | });
27 | }
| - `foo` dropped here while still borrowed
I tried to specify a lifetime for the reference taken by setup_and like so:
fn setup_and<'a, F>(f: F)
where
F: FnOnce(&mut Vec<&'a Foo>) + Send,
{
let mut v: Vec<&'a Foo> = vec![];
Now rustc complains about the setup_and local reference other_foo not living long enough. I assume it is because it wants a larger lifetime than the scope of setup_and.
How would I bind lifetimes correctly in that case ? I would like to express that the references must be valid until the end of the consume call.

You have a serious, serious issue with conflicting lifetimes in your implementation, and there is no simple fix without at least a partial redesign of the outside signatures of your struct and methods. They all stem from the setup_and method, and are highlighted by the compiler when you explicitly described the lifetime bounds.
The body of your method is copied below, with annotations for you to understand the issue:
let mut v: Vec<&Foo> = vec![];
let other_foo = Foo {}; // other_foo is created here
f(&mut v);
v.push(&other_foo); // other_foo requires lifetime 'a to be added to this
consume(v); // consume does not restrict the lifetime requirement 'a
// other_foo is dropped here, at lifetime less than 'a
The easiest solution to this problem is to store an Arc<Foo>, like so (playground):
fn setup_and<F>(f: F)
where
F: FnOnce(&mut Vec<Arc<Foo>>) + Send,
{
let mut v: Vec<Arc<Foo>> = vec![];
let other_foo = Foo {};
f(&mut v);
v.push(Arc::new(other_foo));
consume(&mut v);
}
Arc is an Atomic Reference-Counting pointer. It is a clonable structure that works as a dynamic pointer to an object on the heap; for all intents and purposes, it works as a read-only reference, without the requirement for a lifetime. When all copies of an Arc are dropped, the item inside it is also dropped.
This solves two problems:
Your other_foo is now moved into the Arc, and no longer causes its lifetime issue
You can now access your objects just like you would a reference (Arc implements Deref)
The choice of Arc was made because your FnOnce requires Send, which Rc (the single-threaded variant of Arc) cannot provide.

Related

Borrow checker and closures

So, I have the following problem. I have a structure implementation, and one of the methods of this structure consumes the instance of the structure, which is ok by me, as it is literally the last thing I want to do in my program with this instance (my_struct), but it's totally not OK with the borrow checker:
use std::thread;
struct MyStruct {
some_field: Vec<String>
}
impl MyStruct {
fn new() -> Self {
MyStruct {
some_field: vec!("str".to_string())
}
}
fn do_something_with_self_in_thread(&'static mut self) {
thread::spawn(move || self.some_field = vec!("another_str".to_string()));
}
}
fn main() {
let my_struct: &'static mut MyStruct = &mut MyStruct::new();
my_struct.do_something_with_self_in_thread()
// Some blocking call will follow
}
Error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:20:49
|
20 | let my_struct: &'static mut MyStruct = &mut MyStruct::new();
| --------------------- ^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
21 | my_struct.do_something_with_self_in_thread()
22 | }
| - temporary value is freed at the end of this statement
For more information about this error, try `rustc --explain E0716`.
I tried playing around with lifetimes, but resultless.
How do I get myself out of this situation?
Playground link
When we say a method consumes the instance, then it should take self and not a mutable borrow &mut self in the definition.
i.e.
impl MyStruct {
...
fn do_something_with_self_in_thread(self) {
...
}
}
As commenters have already said, your second problem is with lifetimes. The compiler can't reason about references not outliving owners of resources if they are in separate threads. So to this end the thread::spawn method has trait bounds requiring that the closure is 'static, which means the variables it captures can live as long as they like. Examples which are valid would be owned types T, not references &T or &mut T, unless they are &'static T types. That's not the case in your code as you've instantiated a struct inside main (albeit erroneously annotated as having a 'static lifetime) and then tried to move a mutable borrow of a field inside your closure.

Function works with inferred lifetime but not explicit lifetime

In this code:
struct Obj<'a> {
inside: &'a mut i32
}
fn take_and_return<'o>(obj: Obj<'o>) -> Obj<'o> {
obj
}
fn run_me_1() {
let mut v = 42;
let s: Obj<'_> = Obj {
inside: &mut v
};
take_and_return(s);
}
I wanted to introduce named lifetime for s in run_me_1.
I used Rust Analyzer's suggestion:
fn run_me_2<'a>() {
let mut v = 42;
let s: Obj<'a> = Obj {
inside: &mut v
};
take_and_return(s);
}
But then I got the following error:
error[E0597]: `v` does not live long enough
--> src/lib.rs:20:11
|
17 | fn run_me_2<'a>() {
| -- lifetime `'a` defined here
18 | let mut v = 42;
19 | let s: Obj<'a> = Obj {
| ------- type annotation requires that `v` is borrowed for `'a`
20 | inside: &mut v
| ^^^^^^ borrowed value does not live long enough
...
23 | }
| - `v` dropped here while still borrowed
My understanding is that take_and_return takes ownership of obj, so obj must last forever, so 'o must last forever. That explains why run_me_2 fails to compile.
My questions are:
Why does run_me_1 compile?
What did the inferrer put into that '_ in run_me_1?
How can I fix run_me_2 so that it compiles?
Playground link to code
My understanding is that take_and_return takes ownership of obj, so obj must last forever, so 'o must last forever.
That kind of statement is true if the value is owned and has a 'static lifetime bound, typically expressed as T: 'static. take_and_return doesn't require T: 'static, it takes a concrete type associated with a lifetime 'o which it must not outlive (because it contains a reference with that lifetime).
run_me_1 compiles not because the object is static, but because the variable v clearly outlives the value returned by take_and_return, which is immediately dropped, while v is still live. If you modified run_me_1 so that the value returned by take_and_return() actually outlived v, it would fail to compile too. For example:
fn run_me_1_modified() {
let x;
{
let mut v = 42;
let s: Obj<'_> = Obj { inside: &mut v };
x = take_and_return(s);
}
println!("{:p}", x.inside); // or use x in any other way
}
What did the inferrer put into that '_ in run_me_1?
It put an anonymous lifetime corresponding to the part of the source where v is live.
run_me_2() is a different beast. It basically says, "I will let my caller choose any lifetime it wishes, and then I'll create an Obj associated with that lifetime, after which I'll pass it to take_and_return(), get an Obj with the same lifetime back, and destroy it. This can't be right because Obj { &mut v } clearly requires that the lifetime specified by Obj be a subset of the lifetime of v. But we're technically allowing our caller to choose the lifetime of obj, so it might as well call us with run_me_2<'static>.
Note that a declaration like fn run_me_2<'a>() { ... } doesn't make too much sense because only the static lifetime can be named without referring to a previously defined value. Normally lifetimes are connecting to existing values. The canonical example is something like:
fn search<'a, 'b>(haystack: &'a str, needle: &'b str) -> Option<&'a str>
...which says that the return value will be coming from haystack, and not from needle. On the other hand, fn run_me_2<'a>() is almost useless because it doesn't allow the caller to choose a useful lifetime based on a value it cares about, but only to provide 'static as the only possible name.
How can I fix run_me_2 so that it compiles?
If you insist on having a named lifetime for Obj, then you need to introduce a function that receives a value connected to that lifetime. For example, you could pass the reference to the variable to an inner function:
fn run_me_2() {
fn inner_fn<'a>(r: &'a mut i32) {
let s: Obj<'a> = Obj {
inside: r,
};
take_and_return(s);
}
let mut v = 42;
inner_fn(&mut v);
}

How does the compiler handle borrows when there is a function involving mutable/immutable references as arguments with (nested) lifetimes?

fn func_vecmut<'a>(v: &'a mut Vec<&'a i32>, s: &'a String) {}
fn func_vecmut_fresh_lf<'a, 'b>(v: &'a mut Vec<&'a i32>, s: &'b String) {}
fn func_vec<'a>(v: &'a Vec<&'a i32>, s: &'a String) {}
fn main() {
let mut v = vec![];
{
let s: String = String::from("abc");
/* Note: Below, only one function call is active at a time, other two commented out */
func_vecmut(&mut v, &s); // Understandably, compiler fails this.
func_vecmut_fresh_lf(&mut v, &s); // Understandably, compiler passes this i.e with fresh lifetime for String.
func_vec(&v, &s); // Why does compiler pass this?
}
}
In func_vecmut (as I understand it), the compiler sees that the lifetime of String is same as Vec and the elements (i32) that it holds. And since v lives longer (defined outside the block), it extends the borrow to s beyond the scope of the block (i.e afterlife for s) and hence the following error:
error[E0597]: `s` does not live long enough
--> src/main.rs:13:30
|
13 | func_vecmut(&mut v, &s); // Understandably, compiler fails this.
| ^ borrowed value does not live long enough
...
16 | }
| - `s` dropped here while still borrowed
17 | }
| - borrowed value needs to live until here
The issue can be solved by giving a fresh lifetime to the String argument (see func_vecmut_fresh_lf).
But then, why does the compilation not fail for func_vec(&v, &s)? The only difference being &mut Vec. Is this something to do with the fact that immutable references are Copy, whereas mutable ones are not? If it is, how? The function bodies are empty, what does the compiler infer here?

Why is `&mut &foo` valid but `&mut a_ref_to_foo` is invalid?

In the following example, t1 compiles but t2 does not.
Is there anything special about &mut &stream? I don't think Deref kicks in.
use std::net::TcpStream;
fn t1() {
let stream = TcpStream::connect("127.0.0.1").unwrap();
let a = &mut &stream;
}
fn t2(stream: &TcpStream) {
let a = &mut stream;
}
Playground
9 | fn t2(stream: &TcpStream) {
| ------ use `mut stream` here to make mutable
10 | let a = &mut stream;
| ^^^^^^ cannot borrow mutably
&mut &foo
This does two things - it creates a temporary value of type &Foo, then creates another temporary of type &mut &Foo.
let a_ref_to_foo = &foo;
&mut a_ref_to_foo
This also creates a temporary of type &mut &Foo, but to something that is observable via the variable binding a_ref_to_foo, not another temporary.
The problem is the same as this example:
// Works
String::new().clear();
// Doesn't work
let s = String::new();
s.clear();
When you have a temporary value, you have ownership over it which includes treating it as mutable. Once it's been assigned to a binding, the binding has ownership and you must indicate that it's allowed to be mutated or not.

Confusing error in Rust with trait object lifetime

Can anyone tell what the problem is with the following code? The compiler is complaining about lifetimes, but the error message makes absolutely no sense. I've tried everything I could think of, but nothing seems to help.
use std::borrow::BorrowMut;
trait Trait<'a> {
fn accept(&mut self, &'a u8);
}
struct Impl<'a>{
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl{myref: None})
}
fn user<'a>(obj: &mut Trait<'a>) {}
fn parent<'a>(x: &'a u8) {
let mut pool = new();
user(pool.borrow_mut());
}
The compiler error is
error: `pool` does not live long enough
--> src/wtf.rs:22:10
|
22 | user(pool.borrow_mut());
| ^^^^ does not live long enough
23 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
Which makes absolutely no sense. How is the borrower outliving anything? I'm not even using the borrowed value!
Ok, this does make sense, but it's hard to see due to lifetime elision. So, here's your code with all the lifetimes written out explicitly, and with irrelevant details culled:
use std::borrow::BorrowMut;
trait Trait<'a> {}
struct Impl<'a> {
myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {}
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}
fn parent() {
/* 'i: */ let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */ let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
/* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */ let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
user(pool_borrow);
}
Now, from the perspective of the last line of parent, we can work out the following equivalences by just reading the definition of user and substituting the lifetimes we have in parent:
'a = 'x
'b = 'i
'b = 'x
Furthermore, this lets us conclude that:
'x = 'i
This is the problem. Because of the way you've defined user, you've put yourself in a situation where the lifetime of the pool_ref borrow (which is equal to the lifetime of the pool storage location you're borrowing from) must be the same as the lifetime 'x being used in the thing being stored in pool.
It's a bit like the Box being able to have a pointer to itself before it exists, which doesn't make any sense.
Either way, the fix is simple. Change user to actually have the correct type:
fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}
This matches the type produced by new. Alternately, just don't use borrow_mut:
user(&mut *pool)
This works because it is "re-borrowing". Calling borrow_mut translates the lifetimes more or less directly, but re-borrowing allows the compiler to narrow the borrows to shorter lifetimes. To put it another way, explicitly calling borrow_mut doesn't allow the compiler enough freedom to "fudge" the lifetimes to make them all line up, re-borrowing does.
As a quick aside:
I'm not even using the borrowed value!
Irrelevant. Rust does type- and lifetime-checking entirely locally. It never looks at the body of another function to see what it's doing; it goes on the interface alone. The compiler neither checks, nor cares, what you're doing inside a different function.
Note that there's more to the error message:
error: `pool` does not live long enough
--> src/main.rs:25:10
|>
25 |> user(pool.borrow_mut());
|> ^^^^
note: reference must be valid for the block at 23:25...
--> src/main.rs:23:26
|>
23 |> fn parent<'a>(x: &'a u8) {
|> ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
--> src/main.rs:24:26
|>
24 |> let mut pool = new();
|> ^
Let's look at user:
fn user<'a>(obj: &mut Trait<'a>) {}
This says that it will accept a mutable reference (with an unnamed lifetime) to a trait object parameterized with the lifetime 'a.
Turning to new, I'd say the method is highly suspicious:
fn new<'a>() -> Box<Trait<'a> + 'a> {
Box::new(Impl { myref: None })
}
This says that it will return a boxed trait object with whatever lifetime the caller specifies. That basically never makes sense.
All that said, I'm not clear why the code chooses to use borrow_mut. I would have written that more directly:
user(&mut *pool);
This dereferences the Box<Trait> to get a Trait, then takes a mutable reference, yielding &mut Trait, which compiles.
I cannot currently explain why BorrowMut differs in behavior.
I'm not sure why this error happens, but I can give solutions!
First, it seems that using borrow_mut unnecessarily restricts the lifetime of the returned reference. Using operators to create the reference solves the error.
fn parent() {
let mut pool = new();
user(&mut *pool);
}
However, if we don't do that, we can solve the error by adding a lifetime bound to the Trait object in user's obj argument.
fn user<'a>(obj: &mut (Trait<'a> + 'a)) {}

Resources