Why is this borrow still "active"? - rust

This is a simplified example of something I've encountered:
trait Bla<'a> {
fn create(a: &'a Foo) -> Self;
fn consume(self) -> u8;
}
struct Foo;
impl Foo {
fn take(&mut self, u: u8) {}
}
struct Bar {
foo: Foo,
}
impl Bar {
fn foobar<'a, 'b, B: Bla<'b>>(&'a mut self)
where 'a: 'b {
let u = {
// immutable borrow occurs here
// type annotation requires that `self.foo` is borrowed for `'b`
let foo: &'b Foo = &self.foo;
let b = B::create(foo);
b.consume()
};
// error[E0502]: cannot borrow `self.foo` as mutable because it is also borrowed as immutable
self.foo.take(u);
}
}
Why is self.foo still considered to be borrowed even after exiting the block it was borrowed on, with everything that used the borrow also dropped?

The data might flow to another place.
To understand why the compiler is correct that the immutable borrow might still exist, consider the following implementation of Bla that is valid:
#![feature(once_cell)]
// The data stored in this global static variable exists for the entirety of runtime.
static REFS: SyncLazy<Mutex<Vec<&'static Foo>>> = SyncLazy::new(|| Mutex::new(vec![]));
struct Bad;
impl Bla<'static> for Bad {
fn create(a: &'static Foo) -> Self {
// The reference can go somewhere other than `Self`!
REFS.lock().unwrap().push(a);
Bad
}
fn consume(self) -> u8 {
// Even if `self` is consumed here,
// `self` doesn't necessarily destory the immutable borrow.
0
}
}
When your generic foobar function accepts any valid implementation of Bla, a valid implementation might take a &'static borrow that guarantees the borrow to last for the entire runtime. Therefore, it is an error because consume does not guarantee that the immutable borrow goes away.
You are probably looking for the for<> syntax as that will allow the Bad implementation to exist but will disallow anyone from calling foobar with Bad:
impl Bar {
fn foobar<B: for<'b> Bla<'b>>(&mut self) {
let u = {
let foo: &Foo = &self.foo;
let b = B::create(foo);
b.consume()
};
self.foo.take(u);
}
}
fn main() {
Bar::foobar::<Bad>(&mut Bar { foo: Foo })
// ^ error: implementation of `Bla` is not general enough
// | `Bad` must implement `Bla<'0>`, for any lifetime `'0`...
// | ...but it actually implements `Bla<'static>`
}
If this isn't the case, you might have to provide more implementation details rather than an example causing the diagnostic error.

Normally, when you don't specify your own lifetime, the lifetime of an immutable borrow to a variable ends when the variable goes out of scope. But in your code, you are specifying an explicit lifetime 'b of the immutable borrow.
To the borrow checker, the only thing known about the lifetime 'b is that it is a subset of 'a (due to where 'a: 'b). In particular, the borrow checker will not assume that the scope of the immutable borrow ends at }, because nothing of that sort is specified about 'b.
The compiler actually says this out loud:
// type annotation requires that self.foo is borrowed for 'b
Summary
Why is self.foo still considered to be borrowed even after exiting the block it was borrowed on, with everything that used the borrow also dropped?
Because the lifetime you specify does not end when the block ends. The borrow checker does not care about that some variables related to the borrow goes out of scope.

Related

Rust function which mutates self and then returns an immutable reference, borrow checker says self is still borrowed mutably

I have a function that I would like to mutate self and then return an immutable reference into self. In theory, I would think that the borrow checker could reason that the mutable reference only lives as long as the function body, and so from when the function exits to when it's return value is dropped self is only borrowed immutably. However, the compiler is telling me that self is borrowed mutably for the lifetime of the return value. Here's an example:
#[derive(Debug)]
struct Foo(i32);
impl Foo {
fn doo(&mut self) -> &Self {
self.0 += 1;
self // implicit coercion
}
}
fn main() {
let mut foo = Foo(0);
let bar = foo.doo();
println!("{:?}, {:?}", &foo, bar);
}
In theory I think this could work since &foo and bar are both &Foos so they can coexist, but this give the error message:
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> src/main.rs:14:28
|
13 | let bar = foo.doo();
| --------- mutable borrow occurs here
14 | println!("{:?}, {:?}", &foo, bar);
| ^^^^ --- mutable borrow later used here
| |
| immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
So is there a reason why the return value of doo must continue to borrow self mutably, or is there a way to get the behavior I want?
Lifetime elision comes into play here. The first step to understanding what's happening is to desugar this and make the lifetimes on the Foo::doo method explicit:
fn doo<'a>(&'a mut self) -> &'a Self
From this signature, we can see that the lifetime of the returned reference is the same as the lifetime of the input reference. This means that the returned immutable reference extends the mutable borrow. Rust doesn't have any kind of reference-downgrading mechanism; foo will be considered mutably borrowed as long as the bar reference is alive.
While this should compile, Rust's lifetime system sadly can't figure this situation out (yet(?)). We can turn to the 'nomicon to learn why.
As #cdhowie rightly points out, explicitly annotating the lifetimes for doo, we get
impl Foo {
fn doo<'a>(&'a mut self) -> &'a Self {
self.0 += 1;
&/* 'a */*self
}
}
And when we similarly annotate the rest of the program like this
fn main() {
let mut foo = Foo(0);
let bar: &/* 'mutable_borrow */Foo = Foo::doo(&/* 'mutable_borrow */mut foo);
let baz: &/* 'immutable_borrow */Foo = &/* 'immutable_borrow */foo;
println!("{:?}", bar); // => 'mutable_borrow: 'immutable_borrow 🗲
}
we can see why the borrow checker doesn't think this is valid. In its eyes, bar can live only as long as the mutable reference to foo we pass in. Or rather, for as long as bar lives, that mutable reference has to live. And we can't get a shared reference while a mutable one is still alive.
P.s.: I don't know enough about Polonius to know whether that could compile this, I'd love to hear from someone who does.

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.

How do I get a reference stored in a struct with the reference's original lifetime?

I am trying to run the following code. It was straightforward to understand that the lifetime of the returned value of inner() was not compatible with the 'static lifetime, but how can I make this work without changing the main() function?
struct Wrapper<'a>(&'a str);
impl<'a> Wrapper<'a> {
fn inner(&self) -> &str {
self.0
}
}
pub fn main() {
let x = "hello";
let wrapper = Wrapper(&x);
let _: &'static str = wrapper.inner();
}
error[E0597]: `wrapper` does not live long enough
--> src/main.rs:12:27
|
12 | let _: &'static str = wrapper.inner();
| ^^^^^^^ borrowed value does not live long enough
13 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
I came across this as a example from a course I am following but I am kind of stuck with understanding how to make this work.
All you have to do is change the signature of inner so that the return type matches the type of the Wrapper's field:
impl<'a> Wrapper<'a> {
fn inner(&self) -> &'a str {
// ^^ 'a added here
self.0
}
}
When you initialize the variable wrapper with Wrapper(&x), the compiler infers that its type is Wrapper<'static>, because x has the 'static lifetime (all literal strings have the 'static lifetime). Thus, when you call inner on a Wrapper<'static>, the return type is &'static str.
If we don't add 'a to the signature, then the lifetime of the returned value is the lifetime of the Wrapper itself. This is more apparent if we write out the lifetimes in full:
impl<'a> Wrapper<'a> {
fn inner<'b>(&'b self) -> &'b str {
self.0
}
}
Your Wrapper doesn't have the 'static lifetime because it lives in a local variable; its life will end when the function returns — main isn't special when it comes to lifetimes. Therefore, the returned string slice doesn't have the 'static lifetime either.
Now, maybe you're wondering, "If the original string has the 'static lifetime, why is it valid to return it with a different lifetime?". It's important to understand that lifetimes don't have to be exact; they can be shortened if necessary. The goal of lifetimes is to ensure you don't use a reference after its referent has been dropped; if you claim that a value has a lifetime shorter than it's actually is, it's totally safe, it just (perhaps unnecessarily) restricts how long the reference can be used.

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

Unable to infer lifetime for borrow expression when using a trait with an explicit lifetime

use std::io::BufReader;
struct Foo {
buf: [u8, ..10]
}
trait Bar<'a> {
fn test(&self, arg: BufReader<'a>) {}
}
impl<'a, T: Bar<'a>> Foo {
fn bar(&'a mut self, t: T) {
t.test(BufReader::new(&self.buf));
let b = &mut self.buf;
}
fn baz(&self, t: T) {
t.test(BufReader::new(&self.buf));
}
}
fn main() {}
The code above fails to compile, with the error message:
lifetimes.rs:17:31: 17:40 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
lifetimes.rs:17 t.test(BufReader::new(&self.buf));
^~~~~~~~~
lifetimes.rs:16:5: 18:6 help: consider using an explicit lifetime parameter as shown: fn baz(&'a self, t: T)
lifetimes.rs:16 fn baz(&self, t: T) {
lifetimes.rs:17 t.test(BufReader::new(&self.buf));
lifetimes.rs:18 }
error: aborting due to previous error
However, if I add the named lifetime parameter, I cannot mutable borrow the buf field after calling test, as seen in fn bar. Commenting out the fn baz and trying to compile results in:
lifetimes.rs:13:22: 13:30 error: cannot borrow `self.buf` as mutable because it is also borrowed as immutable
lifetimes.rs:13 let b = &mut self.buf;
^~~~~~~~
lifetimes.rs:12:32: 12:40 note: previous borrow of `self.buf` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.buf` until the borrow ends
lifetimes.rs:12 t.test(BufReader::new(&self.buf));
^~~~~~~~
lifetimes.rs:14:6: 14:6 note: previous borrow ends here
lifetimes.rs:11 fn bar(&'a mut self, t: T) {
lifetimes.rs:12 t.test(BufReader::new(&self.buf));
lifetimes.rs:13 let b = &mut self.buf;
lifetimes.rs:14 }
^
error: aborting due to previous error
My understanding of this is that by adding the named lifetime 'a to the &'a mut self parameter, the reference taken by BufReader has a lifetime as long as the self reference is valid, which is until the end of the function. This conflicts with the mutable borrow of self.buf on the line after.
However, I am not sure why I need the named lifetime parameter on the self. It seems to me that the BufReader reference should be able to only exist for the lifetime of the t.test method call. Is the compiler complaining because the self.buf borrow must be ensured to live only as long as the &self borrow? How would I go about doing that while still only borrowing it for the lifetime of the method call?
Any help in going about fixing this problem and understanding more about the semantics here would be much appreciated!
Update
So I am still looking into this problem, and I have found this test case and this issue that show basically what I am trying to do. I would very much like to understand why the error pointed to by the test case link is an error.
I can see in the issue rustc output that attempts to point out what the error is, but I am having trouble understanding what exactly it is trying to say.
Removing all explicit lifetimes also works. I've found that I only add lifetimes when I'm sure I need them (i.e. to specifiy that two lifetimes should intersect at a given point which can't be known to the compiler).
I'm not sure exactly what you're going for, but this compiles (on rustc 0.13.0-nightly (cc19e3380 2014-12-20 20:00:36 +0000)).
use std::io::BufReader;
struct Foo {
buf: [u8, ..10]
}
trait Bar {
fn test(&self, arg: BufReader) {}
}
impl<T: Bar> Foo {
fn bar(&mut self, t: T) {
t.test(BufReader::new(&self.buf));
let b = &mut self.buf;
}
fn baz(&self, t: T) {
t.test(BufReader::new(&self.buf));
}
}
Edit
I'm going to copy-edit my comment here:
I originally thought that adding a lifetime or generic parameter to the trait / struct / enum was a shorthand for putting it on every method in the trait, but I was wrong. My current understanding is that you add a lifetime to the trait / struct / enum when that item needs to participate in the lifetime, likely because it is storing a reference with that lifetime.
struct Keeper<'a> {
counts: Vec<&'a i32>,
}
impl<'a> Keeper<'a> {
fn add_one(&mut self, count: &'a i32) {
if *count > 5 {
self.counts.push(count);
}
}
fn add_two<'b>(&mut self, count: &'b i32) -> i32 {
*count + 1
}
}
fn main() {
let mut cnt1 = 1;
let mut cnt2 = 2;
let mut k = Keeper { counts: Vec::new() };
k.add_one(&cnt1);
k.add_two(&cnt2);
// cnt1 += 1; // Errors: cannot assign to `cnt1` because it is borrowed
cnt2 += 1; // Just fine
println!("{}, {}", cnt1, cnt2)
}
Here, we've added a lifetime to Keeper because it might store the reference it is given. The borrow checker must assume that the reference is stored for good when we call add_one, so once we call that method, we can no longer mutate the value.
add_two, on the other hand, creates a fresh lifetime that can only be applied to that function invocation, so the borrow checker knows that once the function returns, it is the One True Owner.
The upshot is, if you need to store a reference, then there's nothing you can do at this level. Rust can't make sure you are safe, and that's something it takes seriously.
However, I bet you don't need to store the reference. Move the <'a, T: Bar<'a>> from the impl to the fn and you'll be good to go.
Said another way: I bet you should never have impl<A> if your trait or struct don't require it. Put the generics on the methods instead.
Original
This compiles, but I'm not 100% sure it does what you intended:
impl Foo {
fn baz<'a, T: Bar<'a>>(&'a self, t: T) {
t.test(BufReader::new(&self.buf));
}
}
I fell into this trap myself, so I'll paste what I was told:
Everything in the impl block is parameterized. I've actually never
seen type parameters added to impl blocks themselves that aren't part
of the trait or type definition. It's far more common to parameterize
the individual methods that need it.
Perhaps other comments / answers can help explain in further detail.

Resources