Search code examples
rustborrow-checker

How to fix my code so the HashMap hmap_for_data holds the counts of items in immutable vector aux_vec_i32?


This is a simplified version of code that I written for a leetcode question. In the problem from leetcode I have an immutable input like aux_vec_i32 below and I need to return something more complicated than in the sample code below, but the compiler errors are similar between the leetcode stuff I wrote and what is below. In the end hmap_for_data should hold the counts of unique counts of items in aux_vec_i32.

The current incantation of '&', 'mut', '*' and 'as' strewn in the code is the result of me fighting the compiler and not achieving success by following its suggestions.

use std::collections::HashMap;

fn main(){
    struct Data{
        the_data: i32,
    }
    let aux_vec_i32 = vec![1, 2, 3, 1];
    let mut hmap_for_data: HashMap<i32, Data>=HashMap::new();

    for i in &aux_vec_i32 {
        match hmap_for_data.get(&mut aux_vec_i32[*i as usize]) {
            None => {
                hmap_for_data.insert(
                    aux_vec_i32[*i as usize], 
                    Data {
                        the_data: 1,
                    }
                );
            }
            Some(mut data_to_increment) => {
                data_to_increment.the_data += 1;
            }
        }
    }
}
error[E0502]: cannot borrow `aux_vec_i32` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:38
   |
10 |     for i in &aux_vec_i32 {
   |              ------------
   |              |
   |              immutable borrow occurs here
   |              immutable borrow later used here
11 |         match hmap_for_data.get(&mut aux_vec_i32[*i as usize]) {
   |                                      ^^^^^^^^^^^ mutable borrow occurs here

error[E0596]: cannot borrow `aux_vec_i32` as mutable, as it is not declared as mutable
  --> src/main.rs:11:38
   |
11 |         match hmap_for_data.get(&mut aux_vec_i32[*i as usize]) {
   |                                      ^^^^^^^^^^^ cannot borrow as mutable
   |
help: consider changing this to be mutable
   |
7  |     let mut aux_vec_i32 = vec![1, 2, 3, 1];
   |         +++

error[E0594]: cannot assign to `data_to_increment.the_data`, which is behind a `&` reference
  --> src/main.rs:21:17
   |
20 |             Some(mut data_to_increment) => {
   |                  --------------------- consider changing this binding's type to be: `&mut Data`
21 |                 data_to_increment.the_data += 1;
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `data_to_increment` is a `&` reference, so the data it refers to cannot be written

Here is a link to the playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=535206613151ae0e4507296cf816d2a0.


Solution

  • Here is the modified code, in order to make it pass compilation.

    Taking an element of aux_vec_i32 does not need &mut.

    In order to increment an element of hmap_for_data, we need .get_mut() instead of .get().

    Another formulation is possible with the .entry() API.

    To be honest, I don't understand what this does since there is a kind of mix between indices and content in the vector...

    use std::collections::HashMap;
    
    fn main() {
        #[derive(Debug)]
        struct Data {
            the_data: i32,
        }
        let aux_vec_i32 = vec![1, 2, 3, 1];
        let mut hmap_for_data: HashMap<i32, Data> = HashMap::new();
    
        for i in &aux_vec_i32 {
            if false {
                // original solution
                match hmap_for_data.get_mut(&aux_vec_i32[*i as usize]) {
                    None => {
                        hmap_for_data.insert(
                            aux_vec_i32[*i as usize],
                            Data { the_data: 1 },
                        );
                    }
                    Some(data_to_increment) => {
                        data_to_increment.the_data += 1;
                    }
                }
            } else {
                // using the  .entry()  API
                hmap_for_data
                    .entry(aux_vec_i32[*i as usize])
                    .and_modify(|e| e.the_data += 1)
                    .or_insert_with(|| Data { the_data: 1 });
            }
        }
        println!("{:?}", hmap_for_data);
    }
    /*
    {1: Data { the_data: 1 }, 3: Data { the_data: 1 }, 2: Data { the_data: 2 }}
    */