0

I have a following example that works fine.

use std::collections::HashMap;


#[derive(Debug)]
struct MyStruct {
    map: HashMap<usize, usize>,
    counter: usize,
}


fn process_counter(instance: &mut MyStruct) {
    for &key in instance.map.keys() {
        let value = instance.map[&key];
        instance.counter += value;
    }
}


fn main() {
    let mut instance = MyStruct { 
        map: HashMap::from([(0, 5), (1, 10)]), 
        counter: 0,
    };
    process_counter(&mut instance);  // MyStruct { map: {0: 5, 1: 10}, counter: 15 }
    println!("{:?}", instance);
}

But if I want to take the body of the for-loop away into a separate function I get such error:

 cannot borrow `*instance` as mutable because it is also borrowed as immutable

Here is the code:

use std::collections::HashMap;


#[derive(Debug)]
struct MyStruct {
    map: HashMap<usize, usize>,
    counter: usize,
}


// This function added
fn increment_counter(instance: &mut MyStruct, key: usize) {
    let value = instance.map[&key];
    instance.counter += value;
}


fn process_counter(instance: &mut MyStruct) {
    for &key in instance.map.keys() {
        increment_counter(instance, key);  // The body moved to increment_counter
    }
}


fn main() {
    let mut instance = MyStruct { 
        map: HashMap::from([(0, 5), (1, 10)]), 
        counter: 0,
    };
    process_counter(&mut instance);
    println!("{:?}", instance);
}

The reason of the error is quite clear: the compiler does let me modify instance fully, because I already iterate the keys (that belong to instance) immutably in the loop, so I am not allowed to pass instance as mutable into an external function. But in my real project the code of increment_counter is quite long and complited and it is called multiple times in different places so I would like to keep it in a separate function.

What is the best way to separate the logic of increment_counter inside of process_counter?

Maybe I could iterate keys in a different way or consider different arguments for increment_counter?

I also tried to iterate keys using a vector this way, but I do not like this solution because it is consuming for the performance and memory.

let keys: Vec<usize> = instance.map.keys().cloned().collect();
Fomalhaut
  • 8,590
  • 8
  • 51
  • 95
  • The problem is that Rust can't figure out that your borrowing separate fields across function calls. The solution would usually be to only pass the fields that `increment_counter` needs to touch instead of the whole struct. – isaactfa Jul 01 '23 at 09:28

0 Answers0