I've just started learning Rust and have come across a seemingly bizarre behaviour of HashMap's entry() method. In the below example, the method takes a mutable reference and returns the Entry enum. I am not even capturing and persisting the returned value. But the borrow checker seems to think a mutable reference to "s" is still in scope when the next iteration begins.
let mut window: HashMap<&String, i32> = HashMap::new();
let mut s = "help_me".to_string();
loop {
let p = &mut s; // ERROR
window.entry(p);
}
This shows snippet panics with error:
Line 27, Char 26: cannot borrow `s` as mutable more than once at a time (solution.rs)
|
27 | window.entry(&mut s);
| ^^^^^^ `s` was mutably borrowed here in the previous iteration of the loop
Can someone please explain this behaviour to me?
The map's key type is &String. Let's call the lifetime &'a String.
The entry() method may need insert the key into the map (this is the entire idea of the entry API). Therefore, it takes the same type as the key, that is, &'a String.
As you borrow s, you need the borrow to last for 'a so you can get &'a String from it. So, you borrow it mutably for 'a.
But the next time the loop is executing, while 'a is still active (because the map is still alive), you borrow s again, again for 'a, while it is already mutably borrowed for 'a so you cannot borrow it during that time. This is the error.
You probably want a map of String (not &String), or, even if you do want references, you can instead of doing window.entry(&mut s); to do window.entry(&s); and then it will work because you can borrow a value for a shared reference multiple times.
Related
I've found lots of questions asking about immutable to mutable, but am not quite sure what I am doing wrong in this code.
The error I am getting is:
"cannot borrow values as immutable because it is also borrowed as mutable"
fn modify_vec_get_slice(values: &mut Vec<i32>) -> &[i32] {
// Modify "values" in some way
values.push(5);
// Return a slice into "values" (dummy code)
values.split_at(values.len() / 2).1
}
fn use_slice(slice: &[i32]) {
// Do something immutably with the slice
}
fn use_vec(values: &Vec<i32>) {
// Do something immutably with the vec
}
fn main() {
let mut values = vec![1, 2, 3, 4];
let slice = modify_vec_get_slice(&mut values); // use values mutably
use_vec(&values); // Error here is "&values" underlined in red, even though "values" is used immutably
use_slice(slice); // use slice immutably (which refers to the vec?). Uncommenting this line fixes the error
}
Why can't I use the slice (an immutable reference) when I can use &values (also an immutable reference)? Surely the mutable reference used in "modify_vec_get_slice" has ended by the time I get to the other functions?
The problem is that slice effectively owns a mutable borrow on values. To you it looks like this shouldn't be the case because the reference returned by modify_vec_get_slice() is immutable, but that immutable reference is tied to the lifetime of the mutable reference given to the function. If we explicitly specify the lifetimes that were assumed by the compiler via lifetime elision, this becomes clear:
fn modify_vec_get_slice<'a>(values: &'a mut Vec<i32>) -> &'a [i32] {
The returned reference is only allowed to live as long as the mutable borrow does, therefore extending the lifetime of slice extends the mutable borrow as well. There isn't any kind of implied "reference type downgrading" that happens in this scenario.
The simplest way to address this problem is to split the function into two, one that takes a mutable reference and performs the mutation, and one that takes an immutable reference and computes the bounds of the slice and returns it.
Further reading:
Return immutable reference, taking mutable reference to self
from the borrowing rule:
At any given time, you can have either one mutable reference or any number of immutable references.
Below, I have a mutable borrow of the struct Foo (the whole struct), which means I borrow every field of this struct. However, I can have another borrow to its field in demo function. And I suspect I have 2 mutable references to x.a:
#[derive(Debug)]
struct Foo {
a: i32,
b: i32,
}
fn demo(foo: &mut Foo) {
// the `foo` is mutable borrow
// should have exclusive access to all elements of Foo instance
// However,
let bar = &mut foo.a; // second ref to `x.a`
*bar += 1;
let baz = &foo.b;
println!("{:?}", bar);
println!("{:?}", baz);
}
fn main() {
let mut x = Foo { a: 10, b: 1 };
let foo = &mut x; // ref to x, implies borrowing x.a and x.b
demo(foo);
}
I know we can have disjoint mutable borrow to a struct binding (split borrow reference), but I'm not sure whether splitting the reference to a struct violates the borrow rule.
clarification: above code can compile. My confusion is that it should not compile due to the aforementioned reason. (playground)
I found this link relevant.
When you are borrowing a structure mutably, you are then free to borrow any of the sub-elements mutably.
But I can't find any doc to support it.
The line
let bar = &mut foo.a;
creates a reborrow of the a field of foo. As long as the reborrow is alive, the field can't be accessed via foo.a anymore, but only via bar. After the last use of bar, you can use foo.a again – once the reborrow is released, the original borrow is available again.
Reborrowing is very similar to borrowing itself. While a value is borrowed, you cannot access the original value until the borrow is released. The same happens when reborrowing. This makes sure that at any given time there is only a single active mutable borrow, and the borrowing rules are upheld.
The fact that you are dealing with a struct here is incidental. You can do the same with a mutable borrow to any value, e.g. after
let mut i = 17;
let borrow = &mut i;
let reborrow = &mut *borrow;
*reborrow += 4;
*borrow += 21;
the value of i is 42. This code looks like there are multiple active mutable borrows to i. However, at any given time only one of them can be used. The reborrow is released after it is last used, so the original borrow becomes usable again. If we swap the last two lines in this code, it won't compile anymore, since the reborrow would still be alive when borrow is accessed, which is not allowed.
Unfortunately, the mechanics of reborrowing aren't currently documented in the Rust reference. A related, also not well documented topic are implicit reborrows that happen when a mutable reference is bound to a variable of a type that is already known to be a mutable reference.
let mut set = HashSet::<u32>::new();
let a = set.get(&1);
set.remove(&1);
a.unwrap(); // cannot borrow `set` as mutable because it is also borrowed as immutable
Here is my code. I see what the error means and why rust prevent me doing it.
But I can't find any type annotation in set.rs to make that error happen.
pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T>
where T: Borrow<Q>,
Q: Hash + Eq
This is the declaration of set.get but I can't see anything relevant.
The important part of get is the following: -> Option<&T>.
Contrary to what you seem to be believing, this method gives you a reference, not the actual object (and doing so also establishes an immutable borrow on the item). As a result, when you then remove() said object (and get a mutable borrow over the collection), the reference is evidently immediately invalidated, and as a result, it is impossible for you to unwrap() it.
You may be looking to take() the set element instead of get(). This will return the object and remove the item from the set, instead of returning a reference; it also means that you will no longer need the call to remove() either (playground):
let mut set = HashSet::<u32>::new();
set.insert(1);
let a = set.take(&1).unwrap();
I'm trying to write a simple function which would pop elements from the BinaryHeap satisfying a certain condition. The function looks like this:
fn check_the_queue(mut queue: BinaryHeap<i32>) {
while !queue.is_empty() {
let entry = queue.peek().unwrap();
if *entry <= 0 {
queue.pop();
}
}
When compiling the borrow checker starts complaining:
src/main.rs:52:13: 52:18 error: cannot borrow `queue` as mutable because it is also borrowed as immutable
How can I go around this problem and make the borrow checker happy?
The error message is quite descriptive:
<anon>:8:13: 8:18 error: cannot borrow `queue` as mutable because it is also borrowed as immutable
<anon>:8 queue.pop();
^~~~~
<anon>:5:21: 5:26 note: previous borrow of `queue` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `queue` until the borrow ends
<anon>:5 let entry = queue.peek().unwrap();
^~~~~
<anon>:10:6: 10:6 note: previous borrow ends here
<anon>:4 while !queue.is_empty() {
...
<anon>:10 }
^
The problem is that this borrows queue:
let entry = queue.peek().unwrap();
peek() returns an Option<&T>, that is, an option with a reference to a value of type T. The borrow is valid for as long as entry is alive, which is until the end of the function. It points to something that is stored inside the heap, so it borrows the heap immutably. In other words, as long as entry is alive (until the end of the function), the heap is immutably borrowed.
queue.pop() borrows the heap mutably, so, by the time you get to that, the heap is immutably borrowed AND you attempt to mutably borrow it at the same time.
The borrow checker rules state that you can't borrow something mutably and immutably simultaneously, so you have a problem.
To fix it, find a way to avoid borrowing twice at the same time. For example, you could do this instead:
fn check_the_queue(mut queue: BinaryHeap<i32>) {
while !queue.is_empty() {
if *queue.peek().unwrap() <= 0 {
queue.pop();
}
}
}
That is, just drop the variable entry. This works because by the time you reach queue.pop(), there are no other borrows active, and the borrow checker is happy :)
Lifetimes and borrowing rules may be hard to grok at first, but the steep learning curve pays off over time.
What is my error and how to fix it?
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let mut vals = get_m().iter().peekable();
println!("Saw a {:?}", vals.peek());
}
(playground)
The compiler's error suggests "consider using a let binding" — but I already am:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:6:45
|
6 | let mut vals = get_m().iter().peekable();
| ------- ^ temporary value dropped here while still borrowed
| |
| temporary value created here
7 | println!("Saw a {:?}", vals.peek());
8 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
This is obviously a newbie question -- though I thought I'd written enough Rust at this point that I had a handle on the borrow checker... apparently I haven't.
This question is similar to Using a `let` binding to increase value lifetime, but doesn't involve breaking down an expression into multiple statements, so I don't think the problem is identical.
The problem is that the Peekable iterator lives to the end of the function, but it holds a reference to the vector returned by get_m, which only lasts as long as the statement containing that call.
There are actually a lot of things going on here, so let's take it step by step:
get_m allocates and returns a vector, of type Vec<i8>.
We make the call .iter(). Surprisingly, Vec<i8> has no iter method, nor does it implement any trait that has one. So there are three sub-steps here:
Any method call checks whether its self value implements the Deref trait, and applies it if necessary. Vec<i8> does implement Deref, so we implicitly call its deref method. However, deref takes its self argument by reference, which means that get_m() is now an rvalue appearing in an lvalue context. In this situation, Rust creates a temporary to hold the value, and passes a reference to that. (Keep an eye on this temporary!)
We call deref, yielding a slice of type &[i8] borrowing the vector's elements.
This slice implements the SliceExt trait, which does have an iter method. Finally! This iter also takes its self argument by reference, and returns a std::slice::Iter holding a reference to the slice.
We make the call .peekable(). As before, std::slice::Iter has no peekable method, but it does implement Iterator; IteratorExt is implemented for every Iterator; and IteratorExt does have a peekable method. This takes its self by value, so the Iter is consumed, and we get a std::iter::Peekable back in return, again holding a reference to the slice.
This Peekable is then bound to the variable vals, which lives to the end of the function.
The temporary holding the original Vec<i8>, to whose elements the Peekable refers, now dies. Oops. This is the borrowed value not living long enough.
But the temporary dies there only because that's the rule for temporaries. If we give it a name, then it lasts as long as its name is in scope:
let vec = get_m();
let mut peekable = vec.iter().peekable();
println!("Saw a {:?}", vals.peek());
I think that's the story. What still confuses me, though, is why that temporary doesn't live longer, even without a name. The Rust reference says, "A temporary's lifetime equals the largest lifetime of any reference that points to it." But that's clearly not the case here.
This is happening because you are trying to run your .iter().peekable() on the actual vector inside of get_m(), which is getting re-referenced by vals.
Basically, you want something like this:
fn get_m() -> Vec<i8> {
vec![1, 2, 3]
}
fn main() {
let vals = get_m();
let mut val = vals.iter().peekable();
println!("Saw a {:?}", val.peek());
}
(Playground)
Result:
Saw a Some(1)