Skip to content

Implement ptr_cast_array #144515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,15 @@ impl<T> *const [T] {
}
}

impl<T> *const T {
/// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`.
#[inline]
#[unstable(feature = "ptr_cast_array", issue = "144514")]
pub const fn cast_array<const N: usize>(self) -> *const [T; N] {
self.cast()
}
}

impl<T, const N: usize> *const [T; N] {
/// Returns a raw pointer to the array's buffer.
///
Expand Down
9 changes: 9 additions & 0 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,15 @@ impl<T> *mut [T] {
}
}

impl<T> *mut T {
/// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`.
#[inline]
#[unstable(feature = "ptr_cast_array", issue = "144514")]
pub const fn cast_array<const N: usize>(self) -> *mut [T; N] {
self.cast()
}
}

impl<T, const N: usize> *mut [T; N] {
/// Returns a raw pointer to the array's buffer.
///
Expand Down
7 changes: 7 additions & 0 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ impl<T: Sized> NonNull<T> {
// requirements for a reference.
unsafe { &mut *self.cast().as_ptr() }
}

/// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`.
#[inline]
#[unstable(feature = "ptr_cast_array", issue = "144514")]
pub const fn cast_array<const N: usize>(self) -> NonNull<[T; N]> {
self.cast()
}
}

impl<T: PointeeSized> NonNull<T> {
Expand Down
20 changes: 10 additions & 10 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl<T> [T] {
} else {
// SAFETY: We explicitly check for the correct number of elements,
// and do not let the reference outlive the slice.
Some(unsafe { &*(self.as_ptr().cast::<[T; N]>()) })
Some(unsafe { &*(self.as_ptr().cast_array()) })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this in the libs-api thread before it was accepted but didn't want to be off-topic.
I was wondering how many uses of unsafe ptr casts are really just working around lack of const casting traits.

I think all of chunk methods could also be a fixme(const-hack) for TryFrom not being const before the const trait initiative. So the unsafe could also be removed here eventually.

I thought the other cast_array uses were more motivating outside of this module.

Copy link
Member Author

@scottmcm scottmcm Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, there's a bunch of different ways this could be done. Certainly there's also options like self.as_chunks().0.first() that would work for this function.

I don't think these chunk methods would be TryFrom, though, because the &[T; N] as TryFrom<&[T]> intentionally requires an exact length match. (That's aligned with the general guidance that "from" stuff gives the "same" thing, and having them be eq needs the lengths to be the same.)

But yeah, .get(..N)?.as_array() (https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_array) would be an option if get worked in const. (That's still two checks in the MIR, though, so dunno if it's better that way or not.)

EDIT: Oh, wait, not, the as_chunks version doesn't work because that panics for N == 0. But yes, lots of options. Even split_at_checked(N)?.0.as_array(), I suppose, which doesn't need to wait for const-trait.

Copy link
Contributor

@okaneco okaneco Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of TryInto (unless that has the same issues and I'm just mistaken/misremembering). But as you said, now we have a lot of other options.

}
}

Expand Down Expand Up @@ -361,7 +361,7 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and require exclusive access to the entire slice to mutate the chunk.
Some(unsafe { &mut *(self.as_mut_ptr().cast::<[T; N]>()) })
Some(unsafe { &mut *(self.as_mut_ptr().cast_array()) })
}
}

Expand Down Expand Up @@ -389,7 +389,7 @@ impl<T> [T] {

// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
Some((unsafe { &*(first.as_ptr().cast_array()) }, tail))
}

/// Returns a mutable array reference to the first `N` items in the slice and the remaining
Expand Down Expand Up @@ -422,7 +422,7 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and enforce exclusive mutability of the chunk by the split.
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
Some((unsafe { &mut *(first.as_mut_ptr().cast_array()) }, tail))
}

/// Returns an array reference to the last `N` items in the slice and the remaining slice.
Expand Down Expand Up @@ -450,7 +450,7 @@ impl<T> [T] {

// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
Some((init, unsafe { &*(last.as_ptr().cast_array()) }))
}

/// Returns a mutable array reference to the last `N` items in the slice and the remaining
Expand Down Expand Up @@ -484,7 +484,7 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and enforce exclusive mutability of the chunk by the split.
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
Some((init, unsafe { &mut *(last.as_mut_ptr().cast_array()) }))
}

/// Returns an array reference to the last `N` items in the slice.
Expand Down Expand Up @@ -513,7 +513,7 @@ impl<T> [T] {

// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
Some(unsafe { &*(last.as_ptr().cast_array()) })
}

/// Returns a mutable array reference to the last `N` items in the slice.
Expand Down Expand Up @@ -544,7 +544,7 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and require exclusive access to the entire slice to mutate the chunk.
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
Some(unsafe { &mut *(last.as_mut_ptr().cast_array()) })
}

/// Returns a reference to an element or subslice depending on the type of
Expand Down Expand Up @@ -848,7 +848,7 @@ impl<T> [T] {
#[must_use]
pub const fn as_array<const N: usize>(&self) -> Option<&[T; N]> {
if self.len() == N {
let ptr = self.as_ptr() as *const [T; N];
let ptr = self.as_ptr().cast_array();

// SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length.
let me = unsafe { &*ptr };
Expand All @@ -866,7 +866,7 @@ impl<T> [T] {
#[must_use]
pub const fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]> {
if self.len() == N {
let ptr = self.as_mut_ptr() as *mut [T; N];
let ptr = self.as_mut_ptr().cast_array();

// SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length.
let me = unsafe { &mut *ptr };
Expand Down
Loading