I got a typical borrow checker error and I hope people could show me some idiomatic solution. Below please see a miniature example.
#[derive(Debug)]
pub struct Cell {
ploidy: u32,
}
impl Cell {
fn from_ploidy(ploidy: u32) -> Self {
Cell { ploidy }
}
fn hybrid(&mut self, cell: Cell) { // I would like `cell` to be consumed.
self.ploidy += cell.ploidy;
}
}
fn main() {
let mut cells = vec![
Cell::from_ploidy(46), Cell::from_ploidy(40),
Cell::from_ploidy(46), Cell::from_ploidy(40)
];
println!("{:?}", cells);
// To hybrid every two consecutive cells
// e.g. cells[0].hybrid(cells[1]);
// cells[2].hybrid(cells[3]);
// and let go cells[1] and cells[3]
for i in 0..2usize {
cells[i*2].hybrid(cells[i*2+1]);
}
println!("{:?}", cells);
}
The error is
cannot borrow `cells` as immutable because it is also borrowed as mutable
immutable borrow occurs here
Basically I would like the resources of cells[1]
, cells[3]
to be moved into cells[0]
, cells[2]
while cells[1]
, cells[3]
themselves to be freed from cells
.
If you are using a Vec
and merged cells aren't necessarily adjacent, then you have some complications. You can't just pull values out of any index since Vec
s' elements must always be contiguous. Either remedy of shifting elements in or using swap_remove
to fill from the end you have an issue of your indexes for your pairs no-longer being stable.
Fear not though, there are ways to order things how we like. It would be ideal to order the cells such that the ones being consumed will be at the end and the corresponding ones being merged into at the beginning such that cells[idx]
can just merge with the one from cells.pop()
(thus the ones at the end should be in reverse order).
We can create this ordering by using the code from my answer here How to sort (reorder) a Vec by indices? and create the sort order like so:
let mut cells = vec![
Cell::from_ploidy(0), Cell::from_ploidy(1),
Cell::from_ploidy(2), Cell::from_ploidy(3),
Cell::from_ploidy(4), Cell::from_ploidy(5),
];
let pairs = vec![(1, 5), (0, 3), (4, 2)];
let sort_indicies = Iterator::chain(
pairs.iter().map(|(a, _)| *a),
pairs.iter().map(|(_, b)| *b).rev(),
).collect();
sort_by_indices(&mut cells, sort_indicies);
Then just follow the steps I outlined above to merge the cells:
for i in 0..cells.len()/2 {
let cell_to_merge = cells.pop().unwrap();
cells[i].hybrid(cell_to_merge);
}
And there we're done! Full code and demonstration output available on the playground. You'll see it outputs the merged cells as described by pairs
:
merging 1 with 5
merging 0 with 3
merging 4 with 2
I have a vector of n cells, and among them I randomly pick k pairs (no resampling, of course), then for each pair, fuse the latter into the former, and eventually free the later
If this should be done randomly then we don't need to track the exact pairs we want to merge, we can just shuffle the array and do the last step only!
use rand::thread_rng;
use rand::seq::SliceRandom;
cells.shuffle(&mut thread_rng());
for i in 0..cells.len()/2 {
let cell_to_merge = cells.pop().unwrap();
cells[i].hybrid(cell_to_merge);
}
Once again a demonstration is available on the playground and an example output merged like so:
merging 5 with 0
merging 3 with 4
merging 2 with 1