Rust: access mutable argument in "while let" - loops

But for example in the following code, I want to insert a value into a mutable BTreeMap/Hashmap in each loop, my simple solution triggered an error, what is the elegant way to do that in Rust?
Do I need clone the BTreeMap at the beginning of each loop?
pub fn build_string(word: &str, trie: &mut BTreeMap<&str, &Node>, word_index: usize) {
for char in word.chars() {
let char_label = char.to_string();
let insert_node = Node::new(&char_label, word_index);
let insert_result = trie.insert(&char_label, &insert_node);
println!("char: {}, at index", char);
}
}
error is
error[E0597]: `insert_node` does not live long enough
--> src/lib.rs:69:54
|
65 | pub fn build_string(word: &str, trie: &mut BTreeMap<&str, &Node>, word_index: usize) {
| - let's call the lifetime of this reference `'1`
...
69 | let insert_result = trie.insert(&char_label, &insert_node);
| -------------------------^^^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `insert_node` is borrowed for `'1`
70 | println!("char: {}, at index", char);
71 | }
| - `insert_node` dropped here while still borrowed

The issue is not with your BTreeMap but rather with the lifetime of your Node. You are creating a node inside the method build_string and trying to insert a reference to it into the map. However, once the method build_string goes out of scope, the local insert_node gets dropped but the reference would still exist in the tree. This is not allowed.
Therefore, you could change the BTreeMap to have values of Node instead of &Node. Then you would simply insert the insert_node into the tree which would take ownership of the node thus avoiding any reference leaks

Related

Lifetime and borrowing const arrays [duplicate]

This question already has an answer here:
Getting 'Missing Lifetime specifier' error
(1 answer)
Closed last year.
I'm starting to study Rust and I admit, I have some problems with lifetimes and borrowing. On even days I think I got it and on odd days I get bitten ! Too much C or C++ ? Or perhaps too old ?
;-)
The following code does not compile:
use core::f64::consts::PI;
pub struct TableOscillator {
sample_rate: u32,
table: &[f64],
}
impl TableOscillator {
pub fn new(sample_rate: u32, table: &[f64]) -> TableOscillator {
TableOscillator { sample_rate, table }
}
// Other methods ...
}
fn main() {
const SAMPLE_RATE: u32 = 96000;
let table: [f64; 1024];
for i in 0..table.len() {
let phase = (2.0 * PI * i as f64) / (table.len() as f64);
table[i] = phase.sin();
}
// At this point I would like "table" to be constant and usable by all the following oscillators.
let osc1 = TableOscillator::new(SAMPLE_RATE, &table);
let osc2 = TableOscillator::new(SAMPLE_RATE, &table);
// ...
}
Here is the compiler message:
error[E0106]: missing lifetime specifier
--> src/main.rs:5:12
|
5 | table: &[f64],
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
3 ~ pub struct TableOscillator<'a> {
4 | sample_rate: u32,
5 ~ table: &'a [f64],
|
A bit of explanation: the different oscillators use their "table" members (only read no write).
What is the Rust idiomatic way to fix that ?
Thanks !
In order to do this you need to annotate the lifetime for TableOscillator to tell Rust that the reference needs to live as long as the struct:
pub struct TableOscillator<'a> {
sample_rate: u32,
table: &'a [f64],
}
osc1 and osc2 cannot outlive table, because they're borrowing it.
See Validating References with Lifetimes.

How can a local variable be assigned in previous iteration of loop

Just a question so I can better understand Rust.
Why does this fail:
for event in rulesengine::RuleEvent::into_enum_iter()
{
let mut event_stats: payloads::EventStats;
event_stats.name = rulesengine::RuleEvent::to_string(&event);
event_stats.event_count = event_counters.event_count[event as usize];
event_stats.event_participant_count = event_counters.event_participant_count[event as usize];
event_stats.event_trigger_count = event_counters.event_trigger_count[event as usize];
rules_stats.event_stats.push(event_stats);
}
With:
error[E0382]: assign to part of moved value: `event_stats`
--> src/motion.rs:317:37
|
316 | ... let mut event_stats: payloads::EventStats;
| --------------- move occurs because `event_stats` has type `payloads::EventStats`, which does not implement the `Copy` trait
317 | ... event_stats.name = rulesengine::RuleEvent::to_string(&event).clone();
| ^^^^^^^^^^^^^^^^ value partially assigned here after move
...
323 | ... },
| - value moved here, in previous iteration of loop
error: aborting due to previous error
When this works:
for event in rulesengine::RuleEvent::into_enum_iter()
{
let name = rulesengine::RuleEvent::to_string(&event);
let event_count = event_counters.event_count[event as usize];
let event_participant_count = event_counters.event_participant_count[event as usize];
let event_trigger_count = event_counters.event_trigger_count[event as usize];
let event_stats = payloads::EventStats { name, event_count, event_participant_count, event_trigger_count };
rules_stats.event_stats.push(event_stats);
}
or even better
for event in rulesengine::RuleEvent::into_enum_iter()
{
let event_stats = payloads::EventStats
{
name: rulesengine::RuleEvent::to_string(&event),
event_count: event_counters.event_count[event as usize],
event_participant_count: event_counters.event_participant_count[event as usize],
event_trigger_count: event_counters.event_trigger_count[event as usize],
};
rules_stats.event_stats.push(event_stats);
}
The variable event_stats is mutable yes, but local. It doesn't exist in the "previous iteration of loop" to be moved.
I believe the error is a mis-diagnosis of the problem. There have been compiler bugs in the past where the message incorrectly blames the loop for uninitialized variables. Perhaps this is another one of those cases.
The core issue is you are trying to assign a field to an uninitialized value:
let mut event_stats: payloads::EventStats;
event_stats.name = ...;
You have declared event_stats but have not initialized it. Rust does not allow you to piece-wise initialize a struct, at least not without a lot of extra juggling.
Both your working solutions create event_stats from a struct literal:
let event_stats = payloads::EventStats { name: ..., ... };
which is the correct way to initialize a struct.

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

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();

Passing an immutable reference when a mutable reference exists

I have a for loop that iterates over a slice of Point structs. The Points will have some fields modified in the loop, so the function containing the loop requires a mutable reference to the slice.
The problem arises when I need to pass a (immutable) reference pointing to the slice to a function within the for loop that iterates over the mutable reference:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut grid = vec![];
grid.push(Point { x: 10, y: 10 });
grid.push(Point { x: -1, y: 7 });
calculate_neighbors(&mut grid);
}
fn calculate_neighbors(grid: &mut [Point]) {
for pt in grid.iter_mut() {
pt.x = nonsense_calc(grid);
}
}
#[allow(unused_variables)]
fn nonsense_calc(grid: &[Point]) -> i32 {
unimplemented!();
}
Playground
error[E0502]: cannot borrow `*grid` as immutable because it is also borrowed as mutable
--> src/main.rs:18:30
|
17 | for pt in grid.iter_mut() {
| ---------------
| |
| mutable borrow occurs here
| mutable borrow used here, in later iteration of loop
18 | pt.x = nonsense_calc(grid);
| ^^^^ immutable borrow occurs here
The compiler complains that grid cannot be borrowed as immutable, because a mutable borrow already exists. This is correct, and I can see the problem it is trying to prevent, but how do I achieve what I need to do? Ideally, I do not have to create a copy of the grid, as this can be expensive.
A solution to avoid borrow the array for the iteration would be to use indexes:
fn calculate_neighbors(grid: &mut [Point]) {
for i in 0..grid.len() {
grid[i].x = nonsense_calc(grid);
}
}

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`

Resources