libs-team
libs-team copied to clipboard
array: add unsafe methods to convert slices to array references
Proposal
Problem statement
Currently, the best way to convert a slice of known size to an array reference with the same size (without unnecessary bounds checks) is something like &*(slice.as_ptr() as *const [T; N])
. There is a lot of potentially dangerous unsafe going on there that would need thorough research to ensure it is actually sound.
Motivation, use-cases
From a user perspective, it would be much nicer to have a conversion function whose only safety invariant is that the slice length matches the array length. This is much easier to verify at the call site.
A concrete use case is the implementation of array::split_array_ref()
, which might use slice::split_at_unchecked()
internally - because the sizes of both result slices is constant and known to be correct, converting them back to arrays does not require bounds checks.
Solution sketches
Initial implementation:
impl<T, const N: usize> &[T; N] {
pub unsafe fn from_slice_unchecked(s: &[T]) -> Self {
// SAFETY: caller guarantees that `s` is a slice of `N` elements.
unsafe { &*(s.as_ptr() as *const [T; N]) }
}
}
impl<T, const N: usize> &mut [T; N] {
pub unsafe fn from_mut_slice_unchecked(s: &mut [T]) -> Self {
// SAFETY: caller guarantees that `s` is a slice of `N` elements.
unsafe { &mut *(s.as_ptr() as *mut [T; N]) }
}
}
Unfortunately, calling this is rather awkward: <&[T; N]>::from_slice_unchecked(s)
. It might be better to instead use methods on [T]
, like s.to_array_ref_unchecked()
.
Links and related work
None as yet.
A concrete use case is the implementation of array::split_array_ref(), which might use slice::split_at_unchecked() internally
That doesn't seem like the best motivational example one could choose considering that that API doesn't even exist yet and I think there are still questions around its const generics. And implementation helpers don't need to be public
Well, if it doesn't seem useful enough, I can just make it an internal method and not expose it. I just thought it sounded like something that would be useful for external users as well.
Just saying that there might be a better example how it could be used.
Would TryInto in combination with unwrap_unchecked work for your use case?
FWIW, it looks like the unwrap_unchecked compiles well here: https://rust.godbolt.org/z/eo9189WTb
We discussed this in today's @rust-lang/libs-api meeting. We agree that we should have these. We'd also like to have the matching non-unchecked
versions (even though those are redundant with try_into()
); those versions should return Option
.
Note that for a checked version, there's also
let (array_ref, []) = slice_ref.split_first_chunk() else { panic!("uhoh") };
as of 1.77.
I don't know if that affects the calculus at all for dedicated checked ones.
(Slice methods for an unchecked conversion definitely still make sense.)