Using mutable and immutable refrence at the same time

The following code can compile:

use std::collections::HashMap;

struct MutAndImmut(HashMap<&'static str, Vec<i32>>, HashMap<&'static str, Vec<i32>>);

fn compute_inner_index(data: &mut MutAndImmut) -> &Vec<i32> {
    if !data.1.contains_key("c") {
        data.1.insert("c", vec![]);
    };
    let a_value = &data.0["a"];
    let b_value = &data.0["b"];
    let c_value = data.1.get_mut("c").unwrap();
    if c_value.len() < a_value.len() {
        for i in c_value.len() .. a_value.len() {
            c_value.push(a_value[i] + b_value[i]);
        }
    };
    c_value
}

The computing logic is to comput data.1["c"] with using data.0["a"], data.0["b"]. So it is using immutable reference &data.0["a"], &data.0["b"] and mutable reference data.1["c"] at the same time. But somehow the compiler allow this to pass in this situation.
But the following code can not compile:

fn compute_outer_index(data: &mut MutAndImmut) -> &Vec<i32> {
    if !data.1.contains_key("c") {
        data.1.insert("c", vec![]);
    };
    let a_value = get_data_imm(data, "a");
    let b_value = get_data_imm(data, "b");
    let c_value = get_data_mut(data);
    if c_value.len() < a_value.len() {
        for i in c_value.len() .. a_value.len() {
            c_value.push(a_value[i] + b_value[i]);
        }
    };
    c_value
}

fn get_data_mut(data: &mut MutAndImmut) -> &mut Vec<i32> {
    data.1.get_mut("c").unwrap()
}

fn get_data_imm<'a>(data: &'a MutAndImmut, s: &'static str) -> &'a Vec<i32> {
    &data.0[s]
}

It seems because that the indexing comes from out function get_data_imm rather than &data.0["a"], although the accual operation is the same.
So, if I need the style of the second functon compute_outer_index, how can I do it in a better way? Do I need to using unsafe block? If I am assure that get_data_mut(data) won't influence the immutablly borrowed data a_value and b_value, is it the best way to get that to using unsafe block?

So, if I need the style of the second functon compute_outer_index , how can I do it in a better way? Do I need to using unsafe block?

It is immediate undefined behavior to create an &mut MutAndImmut while another reference to the same MutAndImmut or any of its contents exists. Thus, you absolutely cannot ever have a safe set of functions that can be called like this:

    let a_value = get_data_imm(data, "a");
    let b_value = get_data_imm(data, "b");
    let c_value = get_data_mut(data);

If you were to try to use unsafe to do this, your program would be unsound.

You will have to pick some alternative:

  • Pass data.0 and data.1 to the functions, not all of data.
  • Put the data being modified inside separate RefCells or RwLocks for run-time-checked borrowing (this means that the functions will no longer return bare & references).
  • Some other design entirely.
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.