-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
featureNew feature or requestNew feature or request
Description
I looked at the internals and was wondering if concat_slices can't be a const function instead. I took inspiration from the "internal" concat function:
enum ConcatError {
LenGreaterThanSum,
LenLesserThanSum,
}
/// Soundly concatenates all items in the given slices.
/// References items, because we don't assume [`T`] is [`Copy`].
///
/// # Errors
/// Errors if the length of all items does not match [`LEN`].
const fn concat_slices<'a, const LEN: usize, T: 'a>(
slices: &'a [&'a [T]],
) -> Result<[&'a T; LEN], ConcatError> {
let mut out: [MaybeUninit<&'a T>; LEN] = [MaybeUninit::uninit(); LEN];
let mut out_i = 0;
let mut slice_i = 0;
while slice_i < slices.len() {
let slice = slices[slice_i];
let mut item_i = 0;
while item_i < slice.len() {
// We reached LEN but there's another item to process
if out_i == LEN {
return Err(ConcatError::LenLesserThanSum);
}
// For older Rust versions change to:
// out[out_i] = MaybeUninit::new(&slice[item_i]);
out[out_i].write(&slice[item_i]);
out_i += 1;
item_i += 1;
}
slice_i += 1;
}
// Index should normally not be equal to the length so you might think that
// <= would be applicable here, but remember that after the last item (index) is processed,
// we still increment out_i.
if out_i < LEN {
return Err(ConcatError::LenGreaterThanSum);
}
// SAFETY:
// MaybeUninit<T> has the same size, alignment, and ABI as T.
// <https://doc.rust-lang.org/core/mem/union.MaybeUninit.html#layout-1>
//
// If all MaybeUninits are initialized then it is safe to
// transmute a [MaybeUninit<T>; N] to [T; N].
//
// All items are initialized because we assert that out_i is exactly LEN.
// Each out_i (except the last out_i value) means that the MaybeUninit at that index is initialized.
//
// Similar to:
// <https://doc.rust-lang.org/core/mem/union.MaybeUninit.html#initializing-an-array-element-by-element>
//
// `transmute_copy` is necessary to ignore the size check.
// The Rust compiler considers `[T; LEN]` a "dependent" type because of `LEN`
// and doesn't try to verify it's the same size as `[U; LEN]`.
#[expect(unsafe_code, reason = "see comment")]
Ok(unsafe { core::mem::transmute_copy(&out) })
}I'm a complete newbie to unsafe, so this code might be wrong! I did use miri to test it out with a few inputs and nothing went wrong. I was also told the unnecessary copy transmute_copy would be optimized away (discussed some of the code on the Discord), but I can't verify that.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
featureNew feature or requestNew feature or request