Max888
Max888

Reputation: 3780

Create collection containing references to items in another collection

I have a table organised as columns, and I want to create a vector of rows which are hash maps whose values are mutable references to the equivalent values in the columns, so that I can then iterate over the rows, change the values which will update the values in the original columns. I think it might be to create an iterator of rows, rather than a vector of rows, but I would prefer to first learn how to do a vector (if possible) since this project is for the purpose of me learning rust. Below is what I have so far, but I am getting the error

cannot borrow `columns` as mutable more than once at a time
`columns` was mutably borrowed here in the previous iteration of the loop

which I can't find a solution to. In the below I create columns which holds the original data, rows which should hold references to the data in columns, and then I try to update columns by getting cell from rows and assigning a new value to it.

use std::collections::HashMap;

#[derive(Debug)]
struct Column {
    name: String,
    data: Vec<i32>,
}
fn main() {
    let mut columns = vec![
        Column {
            name: String::from("col1"),
            data: vec![4, 5, 6],
        },
        Column {
            name: String::from("col2"),
            data: vec![7, 8, 9],
        },
    ];
    println!("{:?}", columns);

    let mut rows = Vec::new();
    let columns_len = columns[0].data.len();
    for i in 0..columns_len {
        let mut row = HashMap::new();
        for column in columns.iter_mut() {
            row.insert(&column.name, column.data.get_mut(i).unwrap());
        }
        rows.push(row);
    }
    println!("{:?}", rows);
    let cell = rows
        .get_mut(1)
        .unwrap()
        .get_mut(&String::from("col2"))
        .unwrap();
    println!("{:?}", cell);
    **cell = 99;
    println!("{:?}", columns);
    println!("{:?}", rows);
    println!("{:?}", cell);
}

Upvotes: 1

Views: 471

Answers (1)

rodrigo
rodrigo

Reputation: 98436

Many times it is useful to think of mutable references as exclusive references. That is, instead of thinking that they allow you to modify the value, think of them as guaranteeing that this is the only live reference that can use that valule, and that the mutability thing is just a nice side effect.

With that in mind, if you manage to get a Vec of mutable references to the individual values of your columns, then the columns themselves will be inaccesible (remember, exclusive access).

And when you create cell that is an exclusive reference to the rows vector, then this vector will be inaccessible for the lifetime of cell.

That said, you can create an array of mutable references to the contents of your columns, as long as you are careful with the iterator API (the get_mut() will not do because the compiler cannot assure that you do not use the same index twice):

    // First create the hash maps
    let mut rows = std::iter::repeat_with(|| HashMap::new())
        .take(columns[0].data.len())
        .collect::<Vec<_>>();
    
    // Then fill in the references.
    for c in columns.iter_mut() {
        for (id, d) in c.data.iter_mut().enumerate() {
            rows[id].insert(c.name.as_str(), d);
        }
    }

Note that the rows variable has type: Vec<HashMap<&str, &mut i32>>, that I don't know if it will be so useful.

In practical Rust, many times it is easier to store the index of a value in the Vec instead of the reference to the inner value. You may think that it is not so efficient, but think of what would happen if you push into the vector and it has to reallocate its inner memory!

Upvotes: 1

Related Questions