rustimmutabilitymutableborrow

I want move elements of HashSet[0] to HashSet[1], error[E0502]: cannot borrow `hsets` as mutable because it is also borrowed as immutable


I want move elements of HashSet[0] to HashSet[1]:

Proejct 1: directly remove() insert()

error, can't complie.

use std::collections::HashSet;

fn main() {
    let mut hsets = vec![];

    // first set
    hsets.push(HashSet::new());
    hsets[0].insert("a1");
    hsets[0].insert("a2");

    // second set
    hsets.push(HashSet::new());
    hsets[1].insert("b1");
    hsets[1].insert("b2");

    // I want move elements of HashSet[0] to HashSet[1]
    for &v in hsets[0].iter() {
        hsets[0].remove(v);
        hsets[1].insert(v);
    }

    dbg!(&hsets);
}
error[E0502]: cannot borrow `hsets` as mutable because it is also borrowed as immutable
  --> src/main.rs:17:9
   |
16 |     for &v in hsets[0].iter() {
   |               ---------------
   |               |
   |               immutable borrow occurs here
   |               immutable borrow later used here
17 |         hsets[0].remove(v);
   |         ^^^^^ mutable borrow occurs here

error[E0502]: cannot borrow `hsets` as mutable because it is also borrowed as immutable
error: aborting due to 2 previous errors

Project 2: use tmp vec

correct, but need extra memory! in fact, my hsets data size more than 56G memory! so I hope don't increase extra memory.

use std::collections::HashSet;

fn main() {
    let mut hsets = vec![];

    // first set
    hsets.push(HashSet::new());
    hsets[0].insert("a1");
    hsets[0].insert("a2");

    // second set
    hsets.push(HashSet::new());
    hsets[1].insert("b1");
    hsets[1].insert("b2");

    let mut arr = vec![];
    for &v in hsets[0].iter() {
        arr.push(v);
    }
    for v in arr {
        hsets[0].remove(v);
        hsets[1].insert(v);
    }

    dbg!(&hsets);
}

Proejct 3: use split_at_mut()

correct, but my hsets vec has millions elements. so may be a not good way. thanks the pretzelhammer give the way!

use std::collections::HashSet;

fn main() {
    let mut hsets = vec![];

    // first set
    hsets.push(HashSet::new());
    hsets[0].insert("a1");
    hsets[0].insert("a2");

    // second set
    hsets.push(HashSet::new());
    hsets[1].insert("b1");
    hsets[1].insert("b2");

    dbg!(&hsets);
    assert_eq!(hsets[0].len(), 2);
    assert_eq!(hsets[1].len(), 2);

    // move elements from first set to second set
    let (first, second) = hsets.split_at_mut(1);
    second[0].extend(first[0].drain());

    dbg!(&hsets);
    assert_eq!(hsets[0].len(), 0);
    assert_eq!(hsets[1].len(), 4);
}

Solution

  • correct, but my hsets vec has millions elements. so may be a not good way. thanks

    The split* methods (split_at, split_first, split_last, and their mutable variants) just return slices and references, they execute in constant time, they don't copy anything and they certainly don't allocate, so the size of the vec isn't really a concern.

    If you have a 10 elements vec and split in the middle it'll create two slices which refer the original vector and have lengths of 5 and 5, if your vector has 10 million elements the slices will have lengths of 5000000 and 5000000. Makes no actual difference.