libs-team icon indicating copy to clipboard operation
libs-team copied to clipboard

array: add unsafe methods to convert slices to array references

Open Xiretza opened this issue 2 years ago • 5 comments

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.

Xiretza avatar Jan 15 '23 17:01 Xiretza

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

the8472 avatar Jan 15 '23 18:01 the8472

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.

Xiretza avatar Jan 15 '23 19:01 Xiretza

Just saying that there might be a better example how it could be used.

the8472 avatar Jan 15 '23 19:01 the8472

Would TryInto in combination with unwrap_unchecked work for your use case?

m-ou-se avatar Oct 17 '23 16:10 m-ou-se

FWIW, it looks like the unwrap_unchecked compiles well here: https://rust.godbolt.org/z/eo9189WTb

scottmcm avatar Oct 17 '23 18:10 scottmcm

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.

joshtriplett avatar Jun 04 '24 16:06 joshtriplett

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.)

scottmcm avatar Jun 04 '24 17:06 scottmcm