Mutable borrow seems to outlive its scope - rust

Trying to compile this program I get stuck on the borrow checker:
use std::collections::BTreeMap;
type Object<'a> = BTreeMap<&'a str, i32>;
struct Root<'a>(Object<'a>);
struct Sub<'a>(&'a mut Object<'a>, &'a str);
impl<'a> Root<'a> {
fn borrow_mut(&'a mut self, data: &'a str) -> Sub<'a> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
fn main() {
let mut me = Root(Object::new());
{
me.borrow_mut("data!");
}
me.borrow();
}
(Playground)
I get:
error[E0502]: cannot borrow `me` as immutable because it is also borrowed as mutable
--> src/main.rs:24:5
|
22 | me.borrow_mut("data!");
| -- mutable borrow occurs here
23 | }
24 | me.borrow();
| ^^ immutable borrow occurs here
25 | }
| - mutable borrow ends here
It looks like the mutable borrow should end before me.borrow() but the borrow checker insists that it ends when main ends.
To quickly explain what I'm trying to accomplish:
Make a parent struct to hold data
Make a sub category of data and store it in the parent
Use this builder style pattern to make MongoDB Queries

You are running afoul of a lifetime issue.
There are multiple different lifetimes in your program:
type Object<'a> = BTreeMap<&'a str, i32>; => this is one
&'a mut Object<'a> => there are up to TWO here
struct Sub<'a>(&'a mut Object<'a>, &'a str); => there are up to THREE here
There is, apparently, no reason for the reference to Object<'a> to have the same lifetime than the &str inside the BTreeMap. However, you told the compiler that you wanted both lifetimes to be the same!
When you write:
struct Sub<'a>(&'a mut Object<'a>, &'a str);
you are telling the compiler that:
the lifetime of the &str inside BTreeMap
the lifetime of the reference to Object<'_>
the lifetime of the &str accompanying the Object<'_>
are all one and the same.
You have over-constrained the requirements; and as a result no solution can satisfy them.
Adding one more degree of freedom is sufficient! We'll just make the lifetime of the reference to Object<'_> different from the lifetime of those &str floating around:
struct Sub<'a, 'b: 'a>(&'a mut Object<'b>, &'b str);
impl<'b> Root<'b> {
fn borrow_mut<'a>(&'a mut self, data: &'b str) -> Sub<'a, 'b> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
Note the subtle 'b: 'a:
Object<'b> contains a reference to something whose lifetime is 'b
the lifetime of the reference to Object<'b> (denoted 'a) must be SHORTER than 'b (otherwise you have a reference to something dead?)
Thus, we say that 'b outlives 'a with 'b: 'a.
And that's it. Simply loosening the requirements allow the compiler to allow your code to compile.
Note that in general, if you find yourself writing something like &'a &'a str you are doing it wrong. If you think about it, you will realize that in order to create a reference to something, it must first be. And therefore a reference to an object necessarily has a shorter lifetime than the object itself (ever so slightly).

Related

Rust lifetimes for implementing a trait on nested slices

I want to create a wrapper around (nested) slices for easy operations on multidimensional data, owned by a different struct.
The most basic version of the mutable version of my slice wrapper might look like this:
struct MySliceMut<'a> {
data: Vec<&'a mut [f32]>,
}
impl<'a, 'b> MySliceMut<'a> {
fn get(&'b mut self) -> &'a mut [&'b mut [f32]] {
self.data.as_mut_slice()
}
}
Now if I want to implement a trait, for instance AddAssign, Rust does not seem to infer the lifetime of &mut self from the implementing type. The compiler complains that &mut self might outlive 'a:
impl<'a> AddAssign<MySlice<'a>> for MySliceMut<'a> { // lifetime 'a
fn add_assign(&mut self, rhs: MySlice<'a>) { // lifetime '1
let a = self.get(); // lifetime may not live long enough, '1 must outlive 'a
let b = rhs.get();
// do inplace addition here
}
}
Full Code - Rust Playground
I tried to figure out the issue with the lifetimes, but can't find it. Would the trait impl require any additional annotations?
struct MySlice<'a> {
data: Vec<&'a [f32]>,
}
impl<'a, 'b> MySlice<'a> {
fn get(&'b self) -> &'a [&'b [f32]] {
self.data.as_slice()
}
}
Problem with your code is that fn get(&'b self) returns variable with wrong lifetime. Associated lifetime 'a of MySlice<'a> is lifetime of inner slice. Associated lifetime 'b of fn get(...) is lifetime of the self. So I guess the function probably should return &'b [&'a [f32]] instead.
-- Edited --
Make sure to change fn get(...) of MySliceMut either.

Specifying lifetime causing immutable/mutable borrow conflict

Small example to illustrate the problem. The following compiles:
fn main() {
let value: u32 = 15;
let mut ref_to_value: &u32 = &0;
fill_object(&mut ref_to_value, &value);
println!("referring to value {}", ref_to_value);
}
fn fill_object<'a>(m: &mut &'a u32, v: &'a u32) {
*m = v;
}
Now, the following gives a compile time error about mutable borrow followed by immutable borrow:
fn fill_object<'a>(m: &'a mut &'a u32, v: &'a u32) {
*m = v;
}
fill_object(&mut ref_to_value, &value);
| ----------------- mutable borrow occurs here
5 | println!("referring to value {}", ref_to_value);
| ^^^^^^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
Why? I'm presuming that because I have now specified a lifetime of 'a for the reference to ref_to_value, that mutable reference is now over the entire scope (ie. main). Whereas before, without the 'a lifetime reference, the mutable reference was limited?
I'm looking for clarity on how to think about this.
Your intuition is spot on. With one lifetime,
fn fill_object<'a>(m: &'a mut &'a u32, v: &'a u32) {
*m = v;
}
All three references are required to live for the same length, so if v lives a long time then the mutable reference must as well. This is not intuitive behavior, so it's generally a bad idea to tie together lifetimes like this. If you don't specify any lifetimes, Rust gives each reference a different one implicitly. So the following are equivalent.
fn fill_object(m: &mut &u32, v: &u32)
fn fill_object<'a, 'b, 'c>(m: &'a mut &'b u32, v: &'c u32)
(Note: The inferred lifetime of returned values is a bit more complicated but isn't in play here)
So, your partially-specified lifetimes are equivalent to
fn fill_object<'a>(m: &mut &'a u32, v: &'a u32);
fn fill_object<'a, 'b>(m: &'b mut &'a u32, v: &'a u32);
As a side note, &mut &u32 is a weird type for multiple reasons. Putting aside the fact that u32 is copy (and hence, outside of generics, useless to take immutable references of), a mutable reference to an immutable reference is just confusing. I'm not sure what you're real use case is. If this was just a test example, then sure. But if this is your real program, I recommend considering if you can get off with fill_object(&mut u32, u32), and if you really need the nested reference, you might consider making it a bit easier to swallow with a structure.
struct MyIntegerCell<'a>(&'a u32);
Plus some documentation as to why this is necessary. And then you would have fill_object(&mut MyIntegerCell, u32) or something like that.

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.

Rust: ".. does not live long enough"

Here is the code (with a Rust beginner difficulty):
use std::cell::RefCell;
use std::collections::HashMap;
pub trait TIndex<'a> {
// cut
}
pub struct HashMapIndex<'a> {
pub filter_by_keyword: HashMap<String, &'a str> // simplified (and ugly)
}
impl<'a> TIndex<'a> for HashMapIndex<'a> {
// cut
}
pub struct Matcher<'a> {
pub index: &'a RefCell<Box<dyn TIndex<'a> + 'a>>
}
impl<'a> Matcher<'a> {
pub fn new(index: &'a RefCell<Box<dyn TIndex<'a> + 'a>>) -> Self {
Matcher {
index
}
}
}
pub fn main() {
let index = HashMapIndex {
filter_by_keyword: HashMap::new()
};
let boxed_index: Box<dyn TIndex> = Box::new(index);
let refcell = RefCell::from(boxed_index);
let mut _matcher = Matcher::new(
&refcell // `refcell` does not live long enough
);
}
playground
I'm not sure i understand what is wrong (but something is definitely).
'a is a main() function scope here, index and refcell live until main() exits. matcher accepts a reference to RefCell that lives at least 'a and references to a box that lives at least 'a and point to a TIndex trait object that lives at least 'a and whose internals live at least 'a.
How should it be changed (and what's wrong in lifetimes declarations here)?
PS. I'm getting a compiler hint (at main() closing }):
}
| -
| |
| `refcell` dropped here while still borrowed
| borrow might be used here, when `refcell` is dropped and runs the destructor for type `RefCell<Box<dyn TIndex<'_>>>`
not sure i understand it as refcell is passed by reference (borrowed by _matcher).
not sure i understand it as refcell is passed by reference (borrowed by _matcher).
The problem is that you're defining Matcher such that the lifetimes of the RefCell and the lifetime of the contents of the RefCell must be the same.
This means you're telling rustc the RefCell must live as long what it contains, meaning once you've put the RefCell inside the Matcher... your program can't work anymore, because a container can't rightly outlive its contents.
You need to split your lifetimes so rustc knows how they nest, at the very least you need to give a different lifetime to the &RefCell and its contents -- and tell rustc that the contents outlive the &RefCell:
pub struct Matcher<'a, 'b> {
pub index: &'a RefCell<Box<dyn TIndex<'b> + 'b>>
}
impl<'a, 'b: 'a> Matcher<'a, 'b> {
pub fn new(index: &'a RefCell<Box<dyn TIndex<'b> + 'b>>) -> Self {
Matcher {
index
}
}
}
It might also be a good idea to split the lifetime parameter of the TIndex and the lifetime bound of the TIndex (the contents of the TIndex should outlive it), but it doesn't seem necessary for the repro you've posted here.

What exactly is the logic of a borrow checker in the case of nested references with the same lifetime? [duplicate]

Trying to compile this program I get stuck on the borrow checker:
use std::collections::BTreeMap;
type Object<'a> = BTreeMap<&'a str, i32>;
struct Root<'a>(Object<'a>);
struct Sub<'a>(&'a mut Object<'a>, &'a str);
impl<'a> Root<'a> {
fn borrow_mut(&'a mut self, data: &'a str) -> Sub<'a> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
fn main() {
let mut me = Root(Object::new());
{
me.borrow_mut("data!");
}
me.borrow();
}
(Playground)
I get:
error[E0502]: cannot borrow `me` as immutable because it is also borrowed as mutable
--> src/main.rs:24:5
|
22 | me.borrow_mut("data!");
| -- mutable borrow occurs here
23 | }
24 | me.borrow();
| ^^ immutable borrow occurs here
25 | }
| - mutable borrow ends here
It looks like the mutable borrow should end before me.borrow() but the borrow checker insists that it ends when main ends.
To quickly explain what I'm trying to accomplish:
Make a parent struct to hold data
Make a sub category of data and store it in the parent
Use this builder style pattern to make MongoDB Queries
You are running afoul of a lifetime issue.
There are multiple different lifetimes in your program:
type Object<'a> = BTreeMap<&'a str, i32>; => this is one
&'a mut Object<'a> => there are up to TWO here
struct Sub<'a>(&'a mut Object<'a>, &'a str); => there are up to THREE here
There is, apparently, no reason for the reference to Object<'a> to have the same lifetime than the &str inside the BTreeMap. However, you told the compiler that you wanted both lifetimes to be the same!
When you write:
struct Sub<'a>(&'a mut Object<'a>, &'a str);
you are telling the compiler that:
the lifetime of the &str inside BTreeMap
the lifetime of the reference to Object<'_>
the lifetime of the &str accompanying the Object<'_>
are all one and the same.
You have over-constrained the requirements; and as a result no solution can satisfy them.
Adding one more degree of freedom is sufficient! We'll just make the lifetime of the reference to Object<'_> different from the lifetime of those &str floating around:
struct Sub<'a, 'b: 'a>(&'a mut Object<'b>, &'b str);
impl<'b> Root<'b> {
fn borrow_mut<'a>(&'a mut self, data: &'b str) -> Sub<'a, 'b> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
Note the subtle 'b: 'a:
Object<'b> contains a reference to something whose lifetime is 'b
the lifetime of the reference to Object<'b> (denoted 'a) must be SHORTER than 'b (otherwise you have a reference to something dead?)
Thus, we say that 'b outlives 'a with 'b: 'a.
And that's it. Simply loosening the requirements allow the compiler to allow your code to compile.
Note that in general, if you find yourself writing something like &'a &'a str you are doing it wrong. If you think about it, you will realize that in order to create a reference to something, it must first be. And therefore a reference to an object necessarily has a shorter lifetime than the object itself (ever so slightly).

Resources