Use MaybeUninit instead of writing directly into uninitialized Vec.

This addresses a precondition error which is caught in newer stdlib.
This commit is contained in:
Stu Black 2025-01-08 15:20:51 -05:00
parent 1fcff8f4eb
commit d94fd76d94

View File

@ -10,7 +10,7 @@ use std::cmp::Eq;
use std::collections::VecDeque;
use std::hash::Hash;
use std::mem;
use std::ptr;
use std::slice;
use crate::base::{EdgeId, VertexId};
use crate::Graph;
@ -28,22 +28,16 @@ fn permute_compact<T>(data: &mut Vec<T>, f: impl Fn(usize) -> Option<usize>) {
// TODO: We should benchmark doing this in-place vs. via moving.
let mut new_data: Vec<T> = Vec::with_capacity(data.len());
// TODO: This relies on an implementation detail of Vec (namely, that
// Vec::with_capacity gives us a block that we can read into with
// get_unchecked_mut, even if the index we're accessing is beyond the length
// of the Vec). This seems unlikely to change, but it may ultimately be more
// future-proof to allocate a block of memory, do writes into it manually,
// and pass it to Vec::from_raw_parts.
let buffer: &mut [mem::MaybeUninit<T>] =
unsafe { slice::from_raw_parts_mut(new_data.as_mut_ptr() as _, new_data.capacity()) };
let compacted = data
.drain(..)
.enumerate()
.filter_map(|(old_index, t)| f(old_index).map(|new_index| (new_index, t)));
let mut retained_count = 0;
{
let compacted = data
.drain(..)
.enumerate()
.filter_map(|(old_index, t)| f(old_index).map(|new_index| (new_index, t)));
for (new_index, t) in compacted {
unsafe { ptr::write(new_data.get_unchecked_mut(new_index), t) };
retained_count += 1;
}
for (new_index, t) in compacted {
buffer[new_index].write(t);
retained_count += 1;
}
unsafe { new_data.set_len(retained_count) }; // TODO: Maybe do this after each swap?
*data = new_data;