I have been trying to learn Rust by developing small programs, however the learning curve is more steep than I thought.
I have been playing with the following code snippet which does not work because of a borrowing compiler error.
It’s a simple program, in which you have a dictionary of custom objects. Based on some conditions, some values will be mutated, whilst others will be deleted from the dictionary.
use std::collections::HashMap;
// Define the Custom struct
pub struct Custom {
x: u8,
}
fn main() {
// Create a HashMap with string keys and Custom objects as values
let mut custom_map: HashMap<String, Custom> = HashMap::new();
// Insert some sample data into the map
custom_map.insert("apple".to_string(), Custom { x: 10 });
custom_map.insert("banana".to_string(), Custom { x: 20 });
custom_map.insert("grape".to_string(), Custom { x: 30 });
custom_map.insert("quince".to_string(), Custom { x: 40 }); // Example entry with 'q'
let mut elements_to_be_deleted: Vec<&String> = Vec::new();
// Iterate over the keys and values in the map
for (key, custom) in &mut custom_map {
if key.contains('a') {
// Increment the x property for keys containing 'a'
custom.x += 1;
}
if key.contains('q') {
// Remove entries with 'q' from the map
elements_to_be_deleted.push(key);
}
}
// iterate over the keys that need to be deleted
for key in &elements_to_be_deleted {
custom_map.remove(*key);
}
// Print the updated values
for (key, custom) in &custom_map {
println!("Key: {}, x: {}", key, custom.x);
}
}
The compiler error is the following:
|
21 | for (key, custom) in &mut custom_map {
| --------------- first mutable borrow occurs here
34 | for key in &elements_to_be_deleted {
| ----------------------- first borrow later used here
35 | custom_map.remove(*key);
| ^^^^^^^^^^ second mutable borrow occurs here
Ok, I understand, I cannot have both the mutable variable itself and a mutable reference to it in the same lifecycle.
I changed the above code and offloaded the mutation operations in separate functions
use std::collections::HashMap;
// Define the Custom struct
pub struct Custom {
x: u8,
}
fn main() {
// Create a HashMap with string keys and Custom objects as values
let mut custom_map: HashMap<String, Custom> = HashMap::new();
// Insert some sample data into the map
custom_map.insert("apple".to_string(), Custom { x: 10 });
custom_map.insert("banana".to_string(), Custom { x: 20 });
custom_map.insert("grape".to_string(), Custom { x: 30 });
custom_map.insert("quince".to_string(), Custom { x: 40 }); // Example entry with 'q'
let mut elements_to_be_deleted: Vec<&String> = Vec::new();
// Iterate over the keys and values in the map
for (key, custom) in &custom_map {
if key.contains('q') {
// Remove entries with 'q' from the map
elements_to_be_deleted.push(&key);
}
}
// modify map elements
modifyMapElements(&mut custom_map);
//delete map elements
deleteElementsFromMap(&mut custom_map, elements_to_be_deleted);
// Print the updated values
for (key, custom) in &custom_map {
println!("Key: {}, x: {}", key, custom.x);
}
}
fn deleteElementsFromMap(map: &mut HashMap<String, Custom>, els_to_delete: Vec<&String>) {
for key in elements_to_be_deleted {
map.remove(&key);
}
}
fn modifyMapElements(map: &mut HashMap<String, Custom>) {
for (key, custom) in map {
if key.contains('a') {
// Increment the x property for keys containing 'a'
custom.x += 1;
}
}
}
The compiler error for this snippet is this:
for (key, custom) in &custom_map {
| ----------- immutable borrow occurs here
...
32 | deleteElementsFromMap(&mut custom_map, elements_to_be_deleted);
| ^^^^^^^^^^^^^^^ ---------------------- immutable borrow later used here
| |
| mutable borrow occurs here
I have tried enclosing the for loop which iterates immutably over the map in a separate block, but it solves nothing
{
// Iterate over the keys and values in the map
for (key, custom) in &custom_map {
if key.contains('q') {
// Remove entries with 'q' from the map
elements_to_be_deleted.push(&key);
}
}
}
I get it, you cannot have mutable and immutable references for a variable on the same scopes.
My question is, how to design the code above to use the Rust reference system to my advantage ?
I am more interested in learning how to think and design this piece of code as a Rust developer, so I wouldn’t be particularily interested in workarounds.