Why can I not mutably borrow a variable in two different map functions? - loops

I have an iterator in Rust that loops over a Vec<u8> and applies the same function at two different stages. I do this by chaining a couple of map functions together. Here is the relevant code (where example, example_function_1, and example_function_2 are stand-in variables and functions respectively):
NOTE: example.chunks() is a custom function! Not the default one on slices!
let example = vec![0, 1, 2, 3];
let mut hashers = Cycler::new([example_function_1, example_function_2].iter());
let ret: Vec<u8> = example
//...
.chunks(hashers.len())
.map(|buf| hashers.call(buf))
//...
.map(|chunk| hashers.call(chunk))
.collect();
Here is the code for Cycler:
pub struct Cycler<I> {
orig: I,
iter: I,
len: usize,
}
impl<I> Cycler<I>
where
I: Clone + Iterator,
I::Item: Fn(Vec<u8>) -> Vec<u8>,
{
pub fn new(iter: I) -> Self {
Self {
orig: iter.clone(),
len: iter.clone().count(),
iter,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn reset(&mut self) {
self.iter = self.orig.clone();
}
pub fn call(&mut self, buf: Bytes) -> Bytes {
// It is safe to unwrap because it should indefinietly continue without stopping
self.next().unwrap()(buf)
}
}
impl<I> Iterator for Cycler<I>
where
I: Clone + Iterator,
I::Item: Fn(Vec<u8>) -> Vec<u8>,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
match self.iter.next() {
next => next,
None => {
self.reset();
self.iter.next()
}
}
}
// No size_hint, try_fold, or fold methods
}
What confuses me is that the second time I reference hashers it says this:
error[E0499]: cannot borrow `hashers` as mutable more than once at a time
--> libpressurize/src/password/password.rs:28:14
|
21 | .map(|buf| hashers.call(buf))
| ----- ------- first borrow occurs due to use of `hashers` in closure
| |
| first mutable borrow occurs here
...
28 | .map(|chunk| hashers.call(chunk))
| --- ^^^^^^^ ------- second borrow occurs due to use of `hashers` in closure
| | |
| | second mutable borrow occurs here
| first borrow later used by call
Shouldn't this work because the mutable reference is not used at the same time?
Please let me know if more info/code is needed to answer this.

.map(|buf| hashers.call(buf))
You're probably thinking that in the above line, hashers is mutably borrowed to call it. That's true (since Cycler::call takes &mut self) but it's not what the compiler error is about. In this line, hashers is mutably borrowed to construct the closure |buf| hashers.call(buf), and that borrow lasts as long as the closure does.
Thus, when you write
.map(|buf| hashers.call(buf))
//...
.map(|chunk| hashers.call(chunk))
you are constructing two closures which live at the same time (assuming this is std::iter::Iterator::map) and mutably borrowing hashers for each of them, which is not allowed.
This error is actually protecting you against a side-effect hazard: it's not obvious (in a purely local analysis) what order the side effects of the two call()s will be performed in, because the map()s could do anything they like with the closures. Given the code you wrote, I assume you're doing this on purpose, but the compiler doesn't know that you know what you're doing.
(We can't even predict what the interleaving will be just because they're iterators. Inside of your //... there could be, say a .filter() step which leads to hashers.call(buf) being called several times between each call to hashers.call(chunk), or something else that produces a different number of outputs than inputs.)
If you know that you want the interleaving of side-effects that is “whenever either map() decides to call it”, then you can gain that freedom with a RefCell or other interior mutability, as dianhenglau's answer demonstrates.

Shouldn't this work because the mutable reference is not used at the same time?
No. The rules of references stated that "At any given time, you can have either one mutable reference or any number of immutable references", no matter if it is or isn't used at the same time. See this answer for the reason behind the rules.
As for workaround, since you're sure that the mutations do not occur simultaneously, you can use std::cell::RefCell as explained in this chapter. Modify the code into:
use std::cell::RefCell;
let example = vec![0, 1, 2, 3];
// Remove the "mut", wrap Cycler in RefCell.
let hashers = RefCell::new(Cycler::new([example_function_1, example_function_2].iter()));
let ret: Vec<u8> = example
//...
.chunks(hashers.borrow().len())
// Borrow hashers as immutable inside the closure, then borrow the Cycler as mutable.
.map(|buf| hashers.borrow_mut().call(buf))
//...
.map(|chunk| hashers.borrow_mut().call(chunk))
.collect();

Related

How to return arrays from Rust functions without them being copied?

I have a string of functions that generate arrays and return them up a call stack. Roughly the function signatures are:
fn solutions(...) -> [[u64; M]; N] { /* run iterator on lots of problem sets */ }
fn generate_solutions(...) -> impl Iterator<Item=[u64; M]> { /* call find_solution on different problem sets */ }
fn find_solution(...) -> [u64; M] { /* call validate_candidate with different candidates to find solution */ }
fn validate_candidate(...) -> Option<[u64; M]> {
let mut table = [0; M];
// do compute intensive work
if works { Some(table) } else { None }
}
My understanding was that Rust will not actually copy the arrays up the call stack but optimize the copy away.
But this isn't what I see. When I switch to Vec, I see 20x speed improvement with the only change being [u64;M] to Vec<u64>. So, it is totally copying the arrays over and over.
So why array and not Vec, everyone always asks. Embedded environment. no_std.
How to encourage Rust to optimize these array copies away?
Unfortunately, guaranteed lack of copies is currently an unsolved problem in Rust. To get the characteristics you want, you will need to explicitly pass in storage it should be written into (the “out parameter” pattern):
fn solutions(..., out: &mut [[u64; M]; N]) {...}
fn find_solution(..., out: &mut [u64; M]) {...}
fn validate_candidate(table: &mut [u64; M]) -> bool {
// write into table
works
}
Thus you will also have to find some alternative to Iterator for generate_solutions (since using Iterator implies that all the results can exist at once without overwriting each other).

How to create a mutable Cycle iterator over multiple arrays

My family and I had fun playing Mancala together and I really liked the simplicity of its rules but have a few questions such as "What is the highest possible score". I thought it would be a fun little project to implement in Rust but I am stuck and need help.
There are many rules of how to play Mancala. I want to implement this version: https://www.youtube.com/watch?v=-A-djjimCcM. Knowing the rules of the game makes understanding my problem easier, but it probably isn't required to get it.
This is how a Mancala board looks like:
| |04|04|04|04|04|04| |
|00|-----------------|00|
| |04|04|04|04|04|04| |
Each of the numbers represent a hole. The numbers on the left and right in the bigger boxes represent a "mancala". A mancala is basically a hole where you count your points. The one on your right, is your own mancala, the one on the left is your opponent's mancala. The numbers represent the number of marbles in that specific hole.
In the game, you can select a hole, take all its marbles and then drop one marble in each of the next holes/mancala until you run out of marbles. You skip your opponent's mancala. This is what I am struggling with.
This is how I tried to solve it:
The Mancala board is a struct that has four arrays storing Holes. One for each of the holes on the side of the player and one for their mancala. I want to chain together and cycle through three of these arrays of Holes so I can run an associated function on those Holes (the opponent's mancala gets skipped). This is my code:
pub const STARTING_MARBLES: i8 = 4;
pub const NO_OF_HOLES_OF_EACH_PLAYER: usize = 6;
// There can be two players
#[derive(Debug, Copy, Clone)]
pub enum Player {
A,
B,
}
// A dip in a mancala board that can contain a number of marbles
#[derive(Debug, Copy, Clone)]
struct Hole {
marbles: i8,
}
impl Hole {
// Adds x marbles to the hole
fn add_x(&mut self, x: i8) {
self.marbles += x;
}
// Takes all marbles from the hole and returns their number
fn take_all(&mut self) -> i8 {
let marbles = self.marbles;
self.marbles = 0;
marbles
}
// Returns the number of marbles in the hole
fn count(&self) -> i8 {
self.marbles
}
}
// A mancala board with all its holes and mancalas to count the players points
#[derive(Debug, Copy, Clone)]
pub struct Board {
holes_a: [Hole; NO_OF_HOLES_OF_EACH_PLAYER],
holes_b: [Hole; NO_OF_HOLES_OF_EACH_PLAYER],
mancala_a: [Hole; 1],
mancala_b: [Hole; 1],
}
impl Board {
// Create, initialize and return a new mancala board
pub fn new() -> Self {
let init_hole = Hole {
marbles: STARTING_MARBLES,
};
let holes_a = [init_hole; NO_OF_HOLES_OF_EACH_PLAYER];
let holes_b = [init_hole; NO_OF_HOLES_OF_EACH_PLAYER];
let mancala_a = [Hole { marbles: 0 }];
let mancala_b = [Hole { marbles: 0 }];
Board {
holes_a,
holes_b,
mancala_a,
mancala_b,
}
}
// Take all marbles from the chosen hole and add them to the following holes and the player's mancala
// player: Player whos turn it is
// no: number of the selected hole. The numbering starts with 0 on the very left hole of the player whos turn it is
pub fn choose_hole(mut self, player: Player, no: usize) {
let (mut players_own_holes, other_players_holes, players_mancala) = match player {
Player::A => (self.holes_a, self.holes_b, self.mancala_a),
Player::B => (self.holes_b, self.holes_a, self.mancala_b),
};
let marbles_to_spend = players_own_holes[no].take_all() as usize;
let holes_iter = self
.holes_a
.iter_mut()
.chain(self.mancala_a.iter_mut())
.chain(self.holes_b.iter_mut())
.cycle()
.skip(no + 1)
.take(marbles_to_spend);
for mut hole in holes_iter {
hole.add_x(1);
}
}
}
However I get the following error:
error[E0277]: the trait bound `std::slice::IterMut<'_, Hole>: Clone` is not satisfied
--> src/lib.rs:75:14
|
75 | .cycle()
| ^^^^^ the trait `Clone` is not implemented for `std::slice::IterMut<'_, Hole>`
|
= note: required because of the requirements on the impl of `Clone` for `std::iter::Chain<std::iter::Chain<std::slice::IterMut<'_, Hole>, std::slice::IterMut<'_, Hole>>, std::slice::IterMut<'_, Hole>>`
note: required by a bound in `cycle`
--> /home/batman/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:3262:23
|
3262 | Self: Sized + Clone,
| ^^^^^ required by this bound in `cycle`
I also tried using the into_iter() method instead. I didn't get any errors then but the values of the holes did not change. I guess a copy was created and the method ran on the copy and then the copy went out of scope and it looked like nothing was changed.
The cycle() iterator method works internally by cloning the input iterator, iterating the clone until it returns None, and then replacing the clone with another clone of the input iterator. This requires that the input iterator can be cloned, but an iterator of mutable references to slice elements can't be cloned, because then you'd be able to call next() on the original and the clone and have two mutable references to the same value. This is supposed to be impossible in Rust, so std::slice::IterMut can't be cloned, and therefore you can't use .cycle() on it.
One way to solve this problem would be to alter your data structure. Arrays of one element generally indicate a design problem; you can accomplish the same thing with just a single value, anyway.
To make this problem simpler, just use a single array, with circular indices. Something like this:
| |12|11|10|09|08|07| |
|13|-----------------|06|
| |00|01|02|03|04|05| |
So now your data structure is simply struct Board { holes: [Hole; 14] }.
Traversing this data structure now becomes incredibly simple -- you can just do (0..14).cycle() to get a repeating iterator of array indices.
When using this data structure, we need to handle the game rule of skipping your opponent's mancala as marbles are distributed. We can handle this, along with handling which side of the board to start on, with a simple match, skip, and filter:
let (opponent_mancala_index, start_idx) = match player {
Player::A => (13, 0),
Player::B => (6, 7),
};
let indexes = (0..14)
.cycle()
.skip(no + start_idx)
.filter(|&v| v != opponent_mancala_index)
.take(marbles_to_spend);
for i in indexes {
self.holes[i].add_x(1);
}
You might consider making these special indexes named constants.
Also, note that your Board::choose_hole() function should take &mut self instead of mut self, otherwise you are making changes to a copy and then discarding that copy.

Why cant a static hashmap for a memoizing function be borrowed as mutable?

I'm trying to create a memoization function in Rust. The problem comes when getting a mutable reference for the cache HashMap. I'm not still confident with the type system and I'm struggling a bit.
use std::collections::HashMap;
use std::hash::Hash;
fn memoize<A, B, F>(f: F, cache: &'static HashMap<A, B>) -> impl Fn(A) -> B
where
A: Eq + Hash + Copy,
B: Clone,
F: Fn(A) -> B,
{
move |value: A| {
if !cache.contains_key(&value) {
cache.insert(value, f(value.clone()));
}
let res = cache.get(&value).unwrap();
res.clone()
}
}
The error is:
error[E0596]: cannot borrow immutable borrowed content `**cache` as mutable
--> src/lib.rs:12:13
|
12 | cache.insert(value, f(value.clone()));
| ^^^^^ cannot borrow as mutable
Why cannot a static lifetime parameter be mutable?
A variable is immutable by default in Rust, therefore you cannot mutate a variable that is not declared as mut. The 'static lifetime does not influence the mutability, but only how long the variable lives.
A Fn "[...] can be called repeatedly without mutating state.". And exactly here is the problem. You want to mutate the environment (in this case your HashMap).
You have to use a FnMut to be able to mutate the environment.
If you use the Entry API, you can simplify your code:
use std::collections::HashMap;
use std::hash::Hash;
fn memoize<A, B, F>(f: F, cache: &'static mut HashMap<A, B>) -> impl FnMut(A) -> B
where
A: Eq + Hash + Copy,
B: Clone,
F: Fn(A) -> B,
{
move |value: A| {
let res = cache.entry(value).or_insert_with(|| f(value));
res.clone()
}
}
As a sidenote, if you compile your code with #[feature(nll)] the error message is actually very good.
error[E0596]: cannot borrow `*cache` as mutable, as `Fn` closures cannot mutate their captured variables
--> src/lib.rs:14:13
|
14 | cache.insert(value, f(value.clone()));
| ^^^^^ cannot borrow as mutable
|
help: consider changing this to accept closures that implement `FnMut`

Extracting an archive with progress bar - mutable borrow error

I am trying to extract a .tar.bz file (or .tar.whatever actually) and also be able to have a xx% progress report. So far I have this:
pub fn extract_file_with_progress<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let path = path.as_ref();
let size = fs::metadata(path)?;
let mut f = File::open(path)?;
let decoder = BzDecoder::new(&f);
let mut archive = Archive::new(decoder);
for entry in archive.entries()? {
entry?.unpack_in(".")?;
let pos = f.seek(SeekFrom::Current(0))?;
}
Ok(())
}
The idea is to use pos/size to get the percentage, but compiling the above function gets me the error cannot borrow f as mutable because it is also borrowed as immutable.
I understand what the error means, but I don't really use f as mutable; I only use the seek function to get the current position.
Is there a way to work-around this, either by forcing the compiler to ignore the mutable borrow or by getting the position in some immutable way?
Files are a bit special. The usual read() and seek() and write() methods (defined on the Read, Seek and Write traits) take self by mutable reference:
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
fn seek(&mut self, pos: SeekFrom) -> Result<u64>
fn write(&mut self, buf: &[u8]) -> Result<usize>
However, all mentioned traits are also implemented for &File, i.e. for immutable references to a file:
impl<'a> Read for &'a File
impl<'a> Seek for &'a File
impl<'a> Write for &'a File
So you can modify a file even if you only have a read-only reference to the file. For these implementations, the Self type is &File, so accepting self by mutable reference in fact means accepting a &mut &File, a mutable reference to a reference to a file.
Your code passes &f to BzDecoder::new(), creating an immutable borrow. Later you call f.seek(SeekFrom::Current(0)), which passes f to seek by mutable reference. However, this is not allowed, since you already have an immutable borrow of the file. The solution is to use the Seek implementation on &File instead:
(&mut &f).seek(SeekFrom::Current(0))
or slightly simpler
(&f).seek(SeekFrom::Current(0))
This only creates a second immutable borrow, which is allowed by Rust's rules for references.
I created a playground example demonstrating that this works. If you replace (&f) with f you get the error you originally got.

Why does a File need to be mutable to call Read::read_to_string?

Here's a line from the 2nd edition Rust tutorial:
let mut f = File::open(filename).expect("file not found");
I'm of the assumption that the file descriptor is a wrapper around a number that basically doesn't change and is read-only.
The compiler complains that the file cannot be borrowed mutably, and I'm assuming it's because the method read_to_string takes the instance as the self argument as mutable, but the question is "why"? What is ever going to change about the file descriptor? Is it keeping track of the cursor location or something?
error[E0596]: cannot borrow immutable local variable `fdesc` as mutable
--> main.rs:13:5
|
11 | let fdesc = File::open(fname).expect("file not found");
| ----- consider changing this to `mut fdesc`
12 | let mut fdata = String::new();
13 | fdesc.read_to_string(&mut fdata)
| ^^^^^ cannot borrow mutably
The whole source:
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let fname = &args[2];
println!("Searching for '{}' in file '{}'...", query, fname);
let fdesc = File::open(fname).expect("file not found"); //not mut
let mut fdata = String::new();
fdesc.read_to_string(&mut fdata)
.expect("something went wrong reading the file");
println!("Found: \n{}", fdata);
}
I'm assuming it's because the method read_to_string takes the instance as the self argument as mutable
Yes, that's correct:
fn read_to_string(&mut self, buf: &mut String) -> Result<usize>
The trait method Read::read_to_string takes the receiver as a mutable reference because in general, that's what is needed to implement "reading" from something. You are going to change a buffer or an offset or something.
Yes, an actual File may simply contain an underlying file descriptor (e.g. on Linux or macOS) or a handle (e.g. Windows). In these cases, the operating system deals with synchronizing the access across threads. That's not even guaranteed though — it depends on the platform. Something like Redox might actually have a mutable reference in its implementation of File.
If the Read trait didn't accept a &mut self, then types like BufReader would have to use things like internal mutability, reducing the usefulness of Rust's references.
See also:
Why is it possible to implement Read on an immutable reference to File?

Resources