Skip to content

Commit

Permalink
Rollup merge of rust-lang#83233 - jethrogb:split_array, r=yaahc
Browse files Browse the repository at this point in the history
Implement split_array and split_array_mut

This implements `[T]::split_array::<const N>() -> (&[T; N], &[T])` and `[T; N]::split_array::<const M>() -> (&[T; M], &[T])` and their mutable equivalents. These are another few “missing” array implementations now that const generics are a thing, similar to rust-lang#74373, rust-lang#75026, etc. Fixes rust-lang#74674.

This implements `[T; N]::split_array` returning an array and a slice. Ultimately, this is probably not what we want, we would want the second return value to be an array of length N-M, which will likely be possible with future const generics enhancements. We need to implement the array method now though, to immediately shadow the slice method. This way, when the slice methods get stabilized, calling them on an array will not be automatic through coercion, so we won't have trouble stabilizing the array methods later (cf. into_iter debacle).

An unchecked version of `[T]::split_array` could also be added as in rust-lang#76014. This would not be needed for `[T; N]::split_array` as that can be compile-time checked. Edit: actually, since split_at_unchecked is internal-only it could be changed to be split_array-only.
  • Loading branch information
matthiaskrgr committed Oct 22, 2021
2 parents 45b600c + 4a43976 commit 301a4f2
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 0 deletions.
78 changes: 78 additions & 0 deletions library/core/src/array/mod.rs
Expand Up @@ -500,6 +500,84 @@ impl<T, const N: usize> [T; N] {
// items.
unsafe { collect_into_array_unchecked(&mut self.iter_mut()) }
}

/// Divides one array reference into two at an index.
///
/// The first will contain all indices from `[0, M)` (excluding
/// the index `M` itself) and the second will contain all
/// indices from `[M, N)` (excluding the index `N` itself).
///
/// # Panics
///
/// Panics if `M > N`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let v = [1, 2, 3, 4, 5, 6];
///
/// {
/// let (left, right) = v.split_array_ref::<0>();
/// assert_eq!(left, &[]);
/// assert_eq!(right, &[1, 2, 3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<2>();
/// assert_eq!(left, &[1, 2]);
/// assert_eq!(right, &[3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<6>();
/// assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
/// assert_eq!(right, &[]);
/// }
/// ```
#[unstable(
feature = "split_array",
reason = "return type should have array as 2nd element",
issue = "90091"
)]
#[inline]
pub fn split_array_ref<const M: usize>(&self) -> (&[T; M], &[T]) {
(&self[..]).split_array_ref::<M>()
}

/// Divides one mutable array reference into two at an index.
///
/// The first will contain all indices from `[0, M)` (excluding
/// the index `M` itself) and the second will contain all
/// indices from `[M, N)` (excluding the index `N` itself).
///
/// # Panics
///
/// Panics if `M > N`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let mut v = [1, 0, 3, 0, 5, 6];
/// let (left, right) = v.split_array_mut::<2>();
/// assert_eq!(left, &mut [1, 0][..]);
/// assert_eq!(right, &mut [3, 0, 5, 6]);
/// left[1] = 2;
/// right[1] = 4;
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
/// ```
#[unstable(
feature = "split_array",
reason = "return type should have array as 2nd element",
issue = "90091"
)]
#[inline]
pub fn split_array_mut<const M: usize>(&mut self) -> (&mut [T; M], &mut [T]) {
(&mut self[..]).split_array_mut::<M>()
}
}

/// Pulls `N` items from `iter` and returns them as an array. If the iterator
Expand Down
74 changes: 74 additions & 0 deletions library/core/src/slice/mod.rs
Expand Up @@ -1665,6 +1665,80 @@ impl<T> [T] {
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
}

/// Divides one slice into an array and a remainder slice at an index.
///
/// The array will contain all indices from `[0, N)` (excluding
/// the index `N` itself) and the slice will contain all
/// indices from `[N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let v = &[1, 2, 3, 4, 5, 6][..];
///
/// {
/// let (left, right) = v.split_array_ref::<0>();
/// assert_eq!(left, &[]);
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<2>();
/// assert_eq!(left, &[1, 2]);
/// assert_eq!(right, [3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<6>();
/// assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
/// assert_eq!(right, []);
/// }
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
pub fn split_array_ref<const N: usize>(&self) -> (&[T; N], &[T]) {
let (a, b) = self.split_at(N);
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at)
unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
}

/// Divides one mutable slice into an array and a remainder slice at an index.
///
/// The array will contain all indices from `[0, N)` (excluding
/// the index `N` itself) and the slice will contain all
/// indices from `[N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let mut v = &mut [1, 0, 3, 0, 5, 6][..];

This comment has been minimized.

Copy link
@leonardo-m

leonardo-m Oct 24, 2021

The first mut isn't necessary :-/

/// let (left, right) = v.split_array_mut::<2>();
/// assert_eq!(left, &mut [1, 0]);
/// assert_eq!(right, [3, 0, 5, 6]);
/// left[1] = 2;
/// right[1] = 4;
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
pub fn split_array_mut<const N: usize>(&mut self) -> (&mut [T; N], &mut [T]) {
let (a, b) = self.split_at_mut(N);
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at_mut)
unsafe { (&mut *(a.as_mut_ptr() as *mut [T; N]), b) }
}

/// Returns an iterator over subslices separated by elements that match
/// `pred`. The matched element is not contained in the subslices.
///
Expand Down
33 changes: 33 additions & 0 deletions library/core/tests/array.rs
Expand Up @@ -436,3 +436,36 @@ where
std::panic::set_hook(prev_hook);
result
}

#[test]
fn array_split_array_mut() {
let mut v = [1, 2, 3, 4, 5, 6];

{
let (left, right) = v.split_array_mut::<0>();
assert_eq!(left, &mut []);
assert_eq!(right, &mut [1, 2, 3, 4, 5, 6]);
}

{
let (left, right) = v.split_array_mut::<6>();
assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]);
assert_eq!(right, &mut []);
}
}

#[should_panic]
#[test]
fn array_split_array_ref_out_of_bounds() {
let v = [1, 2, 3, 4, 5, 6];

v.split_array_ref::<7>();
}

#[should_panic]
#[test]
fn array_split_array_mut_out_of_bounds() {
let mut v = [1, 2, 3, 4, 5, 6];

v.split_array_mut::<7>();
}
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Expand Up @@ -70,6 +70,7 @@
#![feature(integer_atomics)]
#![feature(int_roundings)]
#![feature(slice_group_by)]
#![feature(split_array)]
#![feature(trusted_random_access)]
#![feature(unsize)]
#![feature(unzip_option)]
Expand Down
33 changes: 33 additions & 0 deletions library/core/tests/slice.rs
Expand Up @@ -2191,3 +2191,36 @@ mod swap_panics {
x.swap(2, 5);
}
}

#[test]
fn slice_split_array_mut() {
let v = &mut [1, 2, 3, 4, 5, 6][..];

{
let (left, right) = v.split_array_mut::<0>();
assert_eq!(left, &mut []);
assert_eq!(right, [1, 2, 3, 4, 5, 6]);
}

{
let (left, right) = v.split_array_mut::<6>();
assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]);
assert_eq!(right, []);
}
}

#[should_panic]
#[test]
fn slice_split_array_ref_out_of_bounds() {
let v = &[1, 2, 3, 4, 5, 6][..];

v.split_array_ref::<7>();
}

#[should_panic]
#[test]
fn slice_split_array_mut_out_of_bounds() {
let v = &mut [1, 2, 3, 4, 5, 6][..];

v.split_array_mut::<7>();
}

0 comments on commit 301a4f2

Please sign in to comment.