Function works with inferred lifetime but not explicit lifetime - rust

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);
}

Related

Rust lifetime confusion with custom clone function

I think there is a subtle issue with what I'm trying to do here but I can't quite figure out why. I am attempting to clone a Box type (I know the type inside) but I think the lifetime is being propagated out somehow.
struct Thing
{
internal:Box<dyn std::any::Any>
}
impl Thing
{
fn new<T:'static>(u:usize) -> Self
{
return Self
{
internal:Box::new(u)
}
}
fn clone_func<T:'static>(&self) -> Self
{
return Self
{
internal:Box::new(self.internal.downcast_ref::<T>().unwrap().clone())
}
}
}
pub fn main()
{
let a = Thing::new::<usize>(12usize);
let b = a.clone_func::<usize>();
}
Error
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> <source>:19:45
|
15 | fn clone_func<T:'static>(&self) -> Self
| ----- this data with an anonymous lifetime `'_`...
...
19 | internal:Box::new(self.internal.downcast_ref::<T>().unwrap().clone())
| -----------------------^^^^^^^^^^^^------------------------- ...is captured and required to live as long as `'static` here
error: aborting due to previous error
The problem is that you didn't request T to be Clone, so when you called self.internal.downcast_ref::<T>().unwrap().clone(), you actually cloned the reference, and tried to box it1.
Boxing &T works as far as the types are concerned (because it's a Box<dyn Any>), but it fails borrow checking. While T is guaranteed not to contain references to non-static data, the &T refers to the data inside &self which is not 'static, but has the anonymous lifetime of &self. This is actually pointed out by the compiler, but the error doesn't make sense without context.
Simply changing the trait bound to T: Clone + 'static makes the example compile:
fn clone_func<T: Clone + 'static>(&self) -> Self {
return Self {
internal: Box::new(self.internal.downcast_ref::<T>().unwrap().clone()),
};
}
Playground
1
A shared reference is Clone and Copy because once you have one, you're allowed to create more references to the same data. Normally, given a foo: &T and a T: Clone, foo.clone() will resolve to T::clone(foo), which returns a T as expected. But if T isn't Clone, foo.clone() resolves to <&T>::clone(&foo), which returns another &T referring to the same T value as foo, i.e. it "clones" foo itself rather than the intended *foo.
For example, in this snippet, the type of b.clone() is &X, not X (playground):
//#[derive(Clone)]
struct X;
fn main() {
let a = X;
let b = &a;
let () = b.clone(); // note type of b.clone() is &X, not X
}
If you uncomment #[derive(Clone)], the type of b.clone() becomes the expected X.

How to bind reference lifetime to a function local scope

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.

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?

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)) {}

Why doesn't my struct live long enough?

In Rust, I get the following error:
<anon>:14:9: 14:17 error: `mystruct` does not live long enough
<anon>:14 mystruct.update();
^~~~~~~~
<anon>:10:5: 17:6 note: reference must be valid for the lifetime 'a as defined on the block at 10:4...
<anon>:10 {
<anon>:11 let initial = vec![Box::new(1), Box::new(2)];
<anon>:12 let mystruct = MyStruct { v : initial, p : &arg };
<anon>:13
<anon>:14 mystruct.update();
<anon>:15
...
<anon>:12:59: 17:6 note: ...but borrowed value is only valid for the block suffix following statement 1 at 12:58
<anon>:12 let mystruct = MyStruct { v : initial, p : &arg };
<anon>:13
<anon>:14 mystruct.update();
<anon>:15
<anon>:16 mystruct
<anon>:17 }
error: aborting due to previous error
for the following code:
struct MyStruct<'a>
{
v : Vec<Box<i32>>,
p : &'a i32
}
impl<'a> MyStruct<'a>
{
fn new(arg : &'a i32) -> MyStruct<'a>
{
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg };
mystruct.update();
mystruct
}
fn update(&'a mut self)
{
self.p = &self.v.last().unwrap();
}
}
fn main() {
let x = 5;
let mut obj = MyStruct::new(&x);
}
(Playground)
I don't understand why mystruct does not live enough. If I comment out the mystruct.update() line it works fine though. What's more is, if I comment out the body of update the code still fails. Why does calling an empty function which borrows a mutable self changes things?
I don't understand which reference is the one the error talks about. Can somebody explain this?
The reference this error talks about is the one which is implicitly created when you call update(). Because update() takes &'a mut self, it means that it accepts a value of type &'a mut MyStruct<'a>. It means that in theory you should call update() like this:
(&mut mystruct).update();
It would be very inconvenient to write this everywhere, and so Rust is able to automatically insert necessary &s, &muts and *s in order to call a method. This is called "autoreference", and the only place it happens is method invocations/field access.
The problem is the definition of update() method:
impl<'a> MyStruct<'a> {
...
fn update(&'a mut self) { ... }
...
}
Here you are requesting that update() receives the value it is called at via a reference with lifetime 'a, where 'a is the lifetime of the reference stored in the structure.
However, when you have a structure value you're calling this method on, there should be already a reference to i32 you stored in this structure. Hence the lifetime of the structure value is strictly smaller than the lifetime designated by the lifetime parameter, so it is just impossible to construct &'a mut MyStruct<'a> with local variables (as in your case).
The solution is to use &mut self instead of &'a mut self:
fn update(&mut self) { ... }
// essentially equivalent to
fn update<'b>(&'b mut self) where 'a: 'b { ... }
// `'b` is a fresh local lifetime parameter
This way the lifetime of the structure in this method call is not tied to the reference this structure contains and can be smaller.
More in-depth explanation follows below.
By itself your definition is not nonsense. For example:
struct IntRefWrapper<'a> {
value: &'a i32
}
static X: i32 = 12345;
static Y: IntRefWrapper<'static> = IntRefWrapper { value: &X };
impl<'a> IntRefWrapper<'a> {
fn update(&'a self) { ... }
}
Y.update();
Here update() invocation won't cause compilation errors because both lifetimes (of Y and of X, reference to which is contained in Y) are 'static.
Let's consider your example, for comparison:
impl<'a> MyStruct<'a> {
fn new(arg : &'a i32) -> MyStruct<'a> {
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg };
mystruct.update();
mystruct
}
}
Here we have a lifetime parameter, 'a, which is supplied by the caller of the function. For example, the caller could call this function with a static reference:
static X: i32 = 12345;
MyStruct::new(&X); // here &X has static lifetime
However, when update() method is invoked, mystruct lifetime is bounded by the block it is called in:
{
let initial = vec![Box::new(1), Box::new(2)];
let mystruct = MyStruct { v : initial, p : &arg }; // +
// |
mystruct.update(); // |
// |
mystruct // |
}
Naturally, the borrow checker can't prove that this lifetime is the same as the lifetime provided by the caller (and for any possible "external" lifetime it is indeed impossible for them to match), so it throws an error.
When update is defined like this:
fn update(&mut self) { ... }
// or, equivalently
fn update<'b>(&'b mut self) where 'a: 'b { ... }
then when you call it, it is no longer required that the value you call this method on must live exactly as long as 'a - it is sufficient for it to live for any lifetime which is smaller than or equal to 'a - and the lifetime inside the function perfectly matches these requirements. Thus you can call such method on your value, and the compiler won't complain.
Additionally (as noticed in the comments) the following line is indeed invalid and there is no way around it:
self.p = &self.v.last().unwrap();
The borrow check fails here because you're trying to store a reference with lifetime of the structure into the structure itself. In general this can't be done because it has nasty soundness issues. For example, suppose you were indeed able to store this reference into the structure. But now you can't mutate Vec<Box<i32>> in the structure because it may destroy an element which the previously stored references points at, making the code memory unsafe.
It is impossible to check for such things statically, and so it is disallowed on the borrow checking level. In fact, it is just a nice consequence of general borrow checking rules.

Resources