How do I execute a mutable function after immutable? - rust

I'm creating a virtual machine and I'm trying to take two items from the stack. It does it in this order:
PEEK (Get value),
TAKE (Erase from stack),
PEEK (Get value beneath previous value)
However I'm running into an error:
error[E0502]: cannot borrow `self.plate` as mutable because it is also borrowed as immutable
--> src/machine/mod.rs:398:17
|
397 | let apple = self.plate.peek();
| ----------------- immutable borrow occurs here
398 | self.plate.take();
| ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
401 | int: apple.value.int,
| --------------- immutable borrow later used here
Been looking for fixes... However, I couldn't find a way to work around this problem. Here's my code:
(machine/mod.rs):
MERGE => {
let apple = self.plate.peek();
self.plate.take();
let banana = self.plate.peek();
let seasoning = GetValueOfType {
int: apple.value.int,
float: 0.0,
str: banana.value.str.clone(),
};
let edible = PackedIngredients {
istype: SupportedTypes::Int,
value: seasoning,
};
}
(plate/mod.rs)
// This function is really internal since it has no use in instructions.
pub fn peek(&self) -> &Food { return self.area.last().unwrap(); }
// Taken't?
pub fn take(&mut self) {
if self.area.len() == 0 {
panic!("\u{001b}[31mUser attempted to take from an empty plate.\u{001b}[0m")
}
self.area.pop();
return; // Swapped to prevent E0308.
}
(Plate is just the stack)
Tried making the peek function &mut self, but that creates more problems since you can only have one mutable in given area.
Studying various guides online, borrowing, immuts, muts, but couldn't find anything that fits / solves my problem.

You're making an immutable borrow here:
let apple = self.plate.peek();
This is because you're taking an immutable reference to self &self and returning &Food, the borrow checker is not that smart to figure out the &Food doesn't belong to &self and can only assume that you borrowed part of &self.
Then you tried to use mutate self.plate, the problem here is after that you used the apple reference int: apple.value.int which means that apple can't be dropped until that line, which results in the error.
There is 2 solutions to this problem:
Return the struct itself and not a reference
pub fn peek(&self) -> Food { return self.area.last().unwrap(); }
Or let apple get dropped before you call self.plate.peek()
let apple = self.plate.peek();
// since integer is a Copy then this will copy the int value
let int = apple.value.int;
// `apple` will be dropped here since it is no longer use after
self.plate.take();
let banana = self.plate.peek();
let seasoning = GetValueOfType {
int,
float: 0.0,
str: banana.value.str.clone(),
};

Related

How to prevent multiple mutable borrows when wrapping two different but related types in an enum? [duplicate]

This question already has answers here:
Double mutable borrow error in a loop happens even with NLL on
(2 answers)
Closed 11 months ago.
I'm working with a library that offers a RAII transaction API shaped like this:
struct Dataset {}
struct Transaction<'a> {
dataset: &'a mut Dataset,
}
struct Error;
impl Dataset {
fn start_transaction(&mut self) -> Result<Transaction<'_>, Error> {
Ok(Transaction { dataset: self }) // In real life, may also return Err.
}
}
Now I want to write some wrapper code that tries to start a Transaction on a dataset, but if that fails because the underlying data source does not support transactions, it returns the Dataset as-is. (Then everything is eventually resolved to a Dataset through DerefMut implementations, but that's beside the point.)
Here's my attempt:
enum TransactionIfSupported<'a> {
Transaction(Transaction<'a>),
Dataset(&'a mut Dataset),
}
fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
if let Ok(txn) = dataset.start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(dataset)
}
Sadly the borrow checker frowns upon it:
error[E0499]: cannot borrow `*dataset` as mutable more than once at a time
--> src/lib.rs:28:37
|
24 | fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
| -- lifetime `'a` defined here
25 | if let Ok(txn) = dataset.start_transaction() {
| --------------------------- first mutable borrow occurs here
26 | return TransactionIfSupported::Transaction(txn);
| ---------------------------------------- returning this value requires that `*dataset` is borrowed for `'a`
27 | }
28 | TransactionIfSupported::Dataset(dataset)
| ^^^^^^^ second mutable borrow occurs here
I don't quite understand why this happens. If start_transaction returns the Err variant, I'd expect any borrows from the start_transaction call to no longer be in scope after the if block. I'd expect this to hold even if there was no unconditional return statement inside the if.
What's going on and how do I resolve it?
Playground link
It looks like the borrow checker is not smart enough to handle this case yet. The linked question contains a solution for the HashMap case specifically, which will not work here because there is no contains_key equivalent.
Instead, we need some unsafe code to express it:
fn start_transaction_if_supported<'a, 'b>(dataset: &'b mut Dataset) -> TransactionIfSupported<'a, 'b> {
unsafe {
let dataset_ptr: *mut Dataset = dataset;
if let Ok(txn) = (*dataset_ptr).start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(&mut *dataset)
}
}

Cannot use iterator from one method in another because the "borrow might be used when that temporary is dropped"

I have a struct with two methods. The first method returns an iterator over some field. The second method calls the first one and subsequently tries to modify that field (Playground):
struct Container {
vec: Vec<f64>,
}
impl Container {
fn large_positions<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
self.vec
.iter()
.enumerate()
.filter_map(|(i, &f)| if f > 3.14 { Some(i) } else { None })
}
fn negate_first_large_entry(&mut self) {
if let Some(i) = self.large_positions().next() {
self.vec[i] *= -1.0;
}
}
}
The borrow checker complains:
error[E0502]: cannot borrow `self.vec` as mutable because it is also borrowed as immutable
--> src/lib.rs:15:13
|
14 | if let Some(i) = self.large_positions().next() {
| ----------------------
| |
| immutable borrow occurs here
| a temporary with access to the immutable borrow is created here ...
15 | self.vec[i] *= -1.0;
| ^^^^^^^^ mutable borrow occurs here
16 | }
17 | }
| - ... and the immutable borrow might be used here, when that temporary is dropped and runs the destructor for type `impl Iterator`
If I manually inline large_positions, the code compiles so the destructor thing is not a real issue.
Why does the borrow checker know that there is no conflict in the inlined version?
And how can I explain this to the borrow checker in the non-inlined version?
Update:
The following slightly different version of the second function exhibits the same behavior: (Playground)
fn negate_first_large_entry_below_100(&mut self) {
for i in self.large_positions() {
if self.vec[i] < 100.0 {
self.vec[i] *= -1.0;
return;
}
}
}
Here the return statement causes the borrow checker to understand that the borrow of self.vec can be released as soon as the if branch is entered. But again this only works if large_positions is inlined.
Ideally I would like to modify the signature of large_positions in such a way that the borrow checker is still able to perform this clever "early releasing".
You can extrat the iterator into a variable that you can later drop in order to get a &mut afterwards:
fn negate_first_large_entry(&mut self) {
let mut iter = self.large_positions();
if let Some(i) = iter.next() {
drop(iter);
self.vec[i] *= -1.0;
}
}
Playground
For the second method you just need to use a while loop instead of a for loop to perform the same trick:
fn negate_first_large_entry_below_100(&mut self) {
let mut iter = self.large_positions();
while let Some(i) = iter.next() {
if self.vec[i] < 100.0 {
drop(iter);
self.vec[i] *= -1.0;
return;
}
}
}
Playground
(I believe to have now gained a superficial understanding of the underlying problem, so I will formulate this as an answer. Comments are welcome.)
As the error message hints and the answer by Netwave demonstrates, the issue is related to dropping iter. Upon dropping iter, the borrow of self.vec is freed. If this happens before self.vec is modified, then the code borrow-checks successfully.
In the non-inlined version, the drop of iter happens when iter goes out of scope. The compiler is not able to move this forward, because it is not aware whether iter has any user-defined Drop implementation.
In contrast, in the manually inlined version of the code, the compile is able to perform this drop immediately after the last time iter is used. I presume this optimization is possible because the compiler knows here that no custom code is run upon dropping iter (not sure how I could verify this).
A proper solution to the problem would thus be to extend the return type of large_positions, indicating that the returned value may be dropped before it leaves scope. If I understand this proposal correctly, this is currently impossible because such a "TrivialDrop" trait does not exist in Rust.

Get value out of optional HashMap only when present

I have a bit of code that loads a HashMap and then retrieves a value with the map.get(...) method. In my case, it's possible that I may not be able to return a HashMap, so in reality I'm dealing with an Option<HashMap>.
I've managed to isolate my problem in the following snippet:
use std::collections::HashMap;
type MyMap = HashMap<String, String>;
fn get_map() -> Option<MyMap> {
// In the real case, we may or may not be able to return a map
Some(MyMap::new())
}
fn main() {
let res = get_map().and_then(|h| h.get("foo"));
println!("{:?}", res)
}
I get the following compilation error:
error[E0597]: `h` does not live long enough
--> src/main.rs:11:38
|
11 | let res = get_map().and_then(|h| h.get("foo"));
| ^ - `h` dropped here while still borrowed
| |
| borrowed value does not live long enough
12 | println!("{:?}", res)
13 | }
| - borrowed value needs to live until here
(Playground)
I think that I get what's going on here:
The HashMap owns all of its key-value pairs.
When I call h.get(...) it lends me the value.
Because of that the HashMap needs to exist as long as the value exists.
There are really two questions here:
Am I understanding this correctly?
How do I fix this?
Call Option::as_ref. It converts an Option<T> to Option<&T>:
use std::collections::HashMap;
type MyMap = HashMap<String, String>;
fn get_map() -> Option<MyMap> {
// In the real case, we may or may not be able to return a map
Some(MyMap::new())
}
fn main() {
let map = get_map();
let res = map.as_ref().and_then(|h| h.get("foo"));
println!("{:?}", res)
}
What happened is that and_then consumes the Option; so you were trying to hold a reference to the consumed data.
The same rule applies for the returned value of get_map(): if it is not stored in its own variable, it remains a temporary value, to which you cannot hold a reference.

Implementing add_all_at_index in a singly-linked list

I am implementing a singly-linked list. I want to add a method add_all_at_index which will take a new list and an index, insert the new list at the specified index and rearrange the tail of the current list to follow after the new list.
Assume the current list is is [1,2,3,4,5]. Calling add_all_at_index at position 2 with list [8,9,10] should result in [1,2,8,9,10,3,4,5].
I'm especially having trouble in assigning the old tail of the list after the new list. I do not know how to append [3,4,5] to the node with value 10 in the list.
The algorithm I am trying to implement is
Save the current next of the node at (index - 1) in a variable called current_next
Set the next of node at (index - 1) to the head of the list
Iterate to the last of the newly added list and set its next to the current_next.
I am having trouble accomplishing the last step. Below is the code I came up with:
use std::fmt::*;
fn main() {
let list: List<i32> = List::new();
}
#[derive(PartialEq, Debug)]
pub struct Node<T: Debug> {
pub element: T,
pub next: Option<Box<Node<T>>>,
}
#[derive(PartialEq, Debug)]
pub struct List<T: Debug> {
pub head: Option<Node<T>>,
}
impl<T: Debug> List<T> {
pub fn new() -> Self {
List { head: None }
}
pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
if index > 0 {
let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
nth_node.map(|node| {
let current_next = node.next.take(); // I store a reference to the next of nth node,
node.next = list_to_add.head.map(|node| Box::new(node));
// The 3rd step in the algorithm I mentioned above.
let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
last_node.map(|node| node.next = current_next);
});
} else {
self.head = list_to_add.head
}
}
fn get_nth_node_mut(&mut self, n: usize) -> Option<&mut Node<T>> {
let mut nth_node = self.head.as_mut();
for _ in 0..n {
nth_node = match nth_node {
None => return None,
Some(node) => node.next.as_mut().map(|node| &mut **node),
}
}
nth_node
}
pub fn length(&self) -> usize {
let mut count = 0;
let mut current_node = self.head.as_ref();
while let Some(node) = current_node {
count = count + 1;
current_node = node.next.as_ref().map(|node| &**node)
}
count
}
}
The error I am getting is
warning: unused variable: `list`
--> src/main.rs:4:9
|
4 | let list: List<i32> = List::new();
| ^^^^
|
= note: #[warn(unused_variables)] on by default
= note: to avoid this warning, consider using `_list` instead
error[E0500]: closure requires unique access to `self` but `*self` is already borrowed
--> src/main.rs:26:26
|
25 | let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
| ---- borrow occurs here
26 | nth_node.map(|node| {
| ^^^^^^ closure construction occurs here
...
31 | let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
| ---- borrow occurs due to use of `self` in closure
...
34 | } else {
| - borrow ends here
error[E0502]: cannot borrow `**self` as immutable because it is also borrowed as mutable
--> src/main.rs:31:55
|
31 | let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
| ---- ^^^^ immutable borrow occurs here
| |
| mutable borrow occurs here
32 | last_node.map(|node| node.next = current_next);
33 | });
| - mutable borrow ends here
Is this even the right approach to implement add_all_at_index?
I even tried implementing an iterator that returns a mutable reference to a node but I was not able to do that either. I have pasted the entire code in a gist at https://gist.github.com/hardvain/32fca033bb61a5e3bf8bbeeb32fbbd5e
First, the solution:
pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
if index > 0 {
let tail = {
let nth_node = self.get_nth_node_mut(index).take();
nth_node.map(|node| {
let current_next = node.next.take();
node.next = list_to_add.head.map(|node| Box::new(node));
current_next
})
};
if let Some(current_next) = tail {
let n = self.length();
let last_node = self.get_nth_node_mut(n);
last_node.map(|node| node.next = current_next);
}
} else {
self.head = list_to_add.head
}
}
Ugly, right? Right. Getting this working required several changes:
I moved step 3 (reattaching the tail of the list) outside of the closure passed to map, so that nth_node (which borrows self) wouldn't still be alive when you try to borrow self again to get the length.
I therefore had to save current_next, so I had the closure return it, and stored the result of map in a new variable called tail. So tail is an Option<Box<Node<T>>>.
I wrapped the tail-reattaching part in an if let to destructure tail and get current_next back out.
Then, I separated self.get_nth_node_mut(self.length()) into two statements to resolve the remaining borrowing error.
Some followup suggestions:
Using .map() for side effects and then ignoring the return value is unidiomatic. Use if let to run code on the content of an Option.
You're ignoring all the None cases. If you try to call add_all_at_index with an index that is out of range, nothing happens to self and list_to_add is just lost. The function should probably return a Result of some kind, or perhaps a bool.
.take() is for getting an Option<T> out of a &mut Option<T>. It doesn't do anything useful to an Option<&mut T>.
|node| Box::new(node) is just Box::new.
Because the first node is boxed but none of the others are, you will have to write a lot of special case code, and there's not a transparent conversion between a Node and a List.
Using get_nth_node_mut in the implementation of add_all_at_index almost forces you to traverse the list twice. Because it's implemented on List instead of on Node, you can't easily get a reference to the last element of the list, so you end up calling length() (making the total number of traversals 3) and then get_nth_node_mut again to dig up the last element.
Some of the ugliness can be mitigated by careful interface design -- for instance, this method becomes cleaner if List has a split_at_index method -- but some of it is just due to the fact that linked lists are ugly. Particularly ugly in Rust, because the language forbids shared mutable references, even temporarily. You would have to use unsafe to write many linked list operations in Rust the same way you would in C.
If you have not already, please read Learning Rust With Entirely Too Many Linked Lists. This book addresses many of the subtleties that arise when you try to implement a linked list in Rust.

Why does calling a method on a mutable reference involve "borrowing"?

I'm learning Rust and I'm trying to cargo-cult this code into compiling:
use std::vec::Vec;
use std::collections::BTreeMap;
struct Occ {
docnum: u64,
weight: f32,
}
struct PostWriter<'a> {
bytes: Vec<u8>,
occurrences: BTreeMap<&'a [u8], Vec<Occ>>,
}
impl<'a> PostWriter<'a> {
fn new() -> PostWriter<'a> {
PostWriter {
bytes: Vec::new(),
occurrences: BTreeMap::new(),
}
}
fn add_occurrence(&'a mut self, term: &[u8], occ: Occ) {
let occurrences = &mut self.occurrences;
match occurrences.get_mut(term) {
Some(x) => x.push(occ),
None => {
// Add the term bytes to the big vector of all terms
let termstart = self.bytes.len();
self.bytes.extend(term);
// Create a new occurrences vector
let occs = vec![occ];
// Take the appended term as a slice to use as a key
// ERROR: cannot borrow `*occurrences` as mutable more than once at a time
occurrences.insert(&self.bytes[termstart..], occs);
}
}
}
}
fn main() {}
I get an error:
error[E0499]: cannot borrow `*occurrences` as mutable more than once at a time
--> src/main.rs:34:17
|
24 | match occurrences.get_mut(term) {
| ----------- first mutable borrow occurs here
...
34 | occurrences.insert(&self.bytes[termstart..], occs);
| ^^^^^^^^^^^ second mutable borrow occurs here
35 | }
36 | }
| - first borrow ends here
I don't understand... I'm just calling a method on a mutable reference, why would that line involve borrowing?
I'm just calling a method on a mutable reference, why would that line involve borrowing?
When you call a method on an object that's going to mutate the object, you can't have any other references to that object outstanding. If you did, your mutation could invalidate those references and leave your program in an inconsistent state. For example, say that you had gotten a value out of your hashmap and then added a new value. Adding the new value hits a magic limit and forces memory to be reallocated, your value now points off to nowhere! When you use that value... bang goes the program!
In this case, it looks like you want to do the relatively common "append or insert if missing" operation. You will want to use entry for that:
use std::collections::BTreeMap;
fn main() {
let mut map = BTreeMap::new();
{
let nicknames = map.entry("joe").or_insert(Vec::new());
nicknames.push("shmoe");
// Using scoping to indicate that we are done with borrowing `nicknames`
// If we didn't, then we couldn't borrow map as
// immutable because we could still change it via `nicknames`
}
println!("{:?}", map)
}
Because you're calling a method that borrows as mutable
I had a similar question yesterday about Hash, until I noticed something in the docs. The docs for BTreeMap show a method signature for insert starting with fn insert(&mut self..
So when you call .insert, you're implicitly asking that function to borrow the BTreeMap as mutable.

Resources