Skip to content

Commit 8497948

Browse files
authored
Rollup merge of rust-lang#95198 - clarfonthey:get_chunk, r=scottmcm
Add slice::{split_,}{first,last}_chunk{,_mut} This adds to the existing tracking issue for `slice::array_chunks` (rust-lang#74985) under a separate feature, `slice_get_chunk`. Currently, we have the existing `first`/`last` API for slices: ```rust impl [T] { pub const fn first(&self) -> Option<&T>; pub const fn first_mut(&mut self) -> Option<&mut T>; pub const fn last(&self) -> Option<&T>; pub const fn last_mut(&mut self) -> Option<&mut T>; pub const fn split_first(&self) -> Option<(&T, &[T])>; pub const fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>; pub const fn split_last(&self) -> Option<(&T, &[T])>; pub const fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>; } ``` This augments it with a `first_chunk`/`last_chunk` API that allows retrieving multiple elements at once: ```rust impl [T] { pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>; pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>; pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>; pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>; pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>; pub const fn split_first_chunk_mut<const N: usize>(&mut self) -> Option<(&mut [T; N], &mut [T])>; pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>; pub const fn split_last_chunk_mut<const N: usize>(&mut self) -> Option<(&mut [T; N], &mut [T])>; } ``` The code is based off of a copy of the existing API, with the documentation and examples properly modified. Currently, the most common way to perform these kinds of lookups with the existing methods is via `slice.as_chunks::<N>().0[0]` or the worse `slice.as_chunks::<N>().0[slice.len() - N]`, which is substantially less readable than `slice.first_chunk::<N>()` or `slice.last_chunk::<N>()`. ACP: rust-lang/libs-team#69
2 parents 7664dfe + d58dd10 commit 8497948

File tree

1 file changed

+258
-0
lines changed

1 file changed

+258
-0
lines changed

library/core/src/slice/mod.rs

+258
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,264 @@ impl<T> [T] {
319319
if let [.., last] = self { Some(last) } else { None }
320320
}
321321

322+
/// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements.
323+
///
324+
/// # Examples
325+
///
326+
/// ```
327+
/// #![feature(slice_first_last_chunk)]
328+
///
329+
/// let u = [10, 40, 30];
330+
/// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
331+
///
332+
/// let v: &[i32] = &[10];
333+
/// assert_eq!(None, v.first_chunk::<2>());
334+
///
335+
/// let w: &[i32] = &[];
336+
/// assert_eq!(Some(&[]), w.first_chunk::<0>());
337+
/// ```
338+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
339+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
340+
#[inline]
341+
pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> {
342+
if self.len() < N {
343+
None
344+
} else {
345+
// SAFETY: We explicitly check for the correct number of elements,
346+
// and do not let the reference outlive the slice.
347+
Some(unsafe { &*(self.as_ptr() as *const [T; N]) })
348+
}
349+
}
350+
351+
/// Returns a mutable reference to the first `N` elements of the slice,
352+
/// or `None` if it has fewer than `N` elements.
353+
///
354+
/// # Examples
355+
///
356+
/// ```
357+
/// #![feature(slice_first_last_chunk)]
358+
///
359+
/// let x = &mut [0, 1, 2];
360+
///
361+
/// if let Some(first) = x.first_chunk_mut::<2>() {
362+
/// first[0] = 5;
363+
/// first[1] = 4;
364+
/// }
365+
/// assert_eq!(x, &[5, 4, 2]);
366+
/// ```
367+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
368+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
369+
#[inline]
370+
pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
371+
if self.len() < N {
372+
None
373+
} else {
374+
// SAFETY: We explicitly check for the correct number of elements,
375+
// do not let the reference outlive the slice,
376+
// and require exclusive access to the entire slice to mutate the chunk.
377+
Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) })
378+
}
379+
}
380+
381+
/// Returns the first `N` elements of the slice and the remainder,
382+
/// or `None` if it has fewer than `N` elements.
383+
///
384+
/// # Examples
385+
///
386+
/// ```
387+
/// #![feature(slice_first_last_chunk)]
388+
///
389+
/// let x = &[0, 1, 2];
390+
///
391+
/// if let Some((first, elements)) = x.split_first_chunk::<2>() {
392+
/// assert_eq!(first, &[0, 1]);
393+
/// assert_eq!(elements, &[2]);
394+
/// }
395+
/// ```
396+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
397+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
398+
#[inline]
399+
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
400+
if self.len() < N {
401+
None
402+
} else {
403+
// SAFETY: We manually verified the bounds of the split.
404+
let (first, tail) = unsafe { self.split_at_unchecked(N) };
405+
406+
// SAFETY: We explicitly check for the correct number of elements,
407+
// and do not let the references outlive the slice.
408+
Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail))
409+
}
410+
}
411+
412+
/// Returns a mutable reference to the first `N` elements of the slice and the remainder,
413+
/// or `None` if it has fewer than `N` elements.
414+
///
415+
/// # Examples
416+
///
417+
/// ```
418+
/// #![feature(slice_first_last_chunk)]
419+
///
420+
/// let x = &mut [0, 1, 2];
421+
///
422+
/// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
423+
/// first[0] = 3;
424+
/// first[1] = 4;
425+
/// elements[0] = 5;
426+
/// }
427+
/// assert_eq!(x, &[3, 4, 5]);
428+
/// ```
429+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
430+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
431+
#[inline]
432+
pub const fn split_first_chunk_mut<const N: usize>(
433+
&mut self,
434+
) -> Option<(&mut [T; N], &mut [T])> {
435+
if self.len() < N {
436+
None
437+
} else {
438+
// SAFETY: We manually verified the bounds of the split.
439+
let (first, tail) = unsafe { self.split_at_mut_unchecked(N) };
440+
441+
// SAFETY: We explicitly check for the correct number of elements,
442+
// do not let the reference outlive the slice,
443+
// and enforce exclusive mutability of the chunk by the split.
444+
Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail))
445+
}
446+
}
447+
448+
/// Returns the last `N` elements of the slice and the remainder,
449+
/// or `None` if it has fewer than `N` elements.
450+
///
451+
/// # Examples
452+
///
453+
/// ```
454+
/// #![feature(slice_first_last_chunk)]
455+
///
456+
/// let x = &[0, 1, 2];
457+
///
458+
/// if let Some((last, elements)) = x.split_last_chunk::<2>() {
459+
/// assert_eq!(last, &[1, 2]);
460+
/// assert_eq!(elements, &[0]);
461+
/// }
462+
/// ```
463+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
464+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
465+
#[inline]
466+
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
467+
if self.len() < N {
468+
None
469+
} else {
470+
// SAFETY: We manually verified the bounds of the split.
471+
let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) };
472+
473+
// SAFETY: We explicitly check for the correct number of elements,
474+
// and do not let the references outlive the slice.
475+
Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init))
476+
}
477+
}
478+
479+
/// Returns the last and all the rest of the elements of the slice, or `None` if it is empty.
480+
///
481+
/// # Examples
482+
///
483+
/// ```
484+
/// #![feature(slice_first_last_chunk)]
485+
///
486+
/// let x = &mut [0, 1, 2];
487+
///
488+
/// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() {
489+
/// last[0] = 3;
490+
/// last[1] = 4;
491+
/// elements[0] = 5;
492+
/// }
493+
/// assert_eq!(x, &[5, 3, 4]);
494+
/// ```
495+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
496+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
497+
#[inline]
498+
pub const fn split_last_chunk_mut<const N: usize>(
499+
&mut self,
500+
) -> Option<(&mut [T; N], &mut [T])> {
501+
if self.len() < N {
502+
None
503+
} else {
504+
// SAFETY: We manually verified the bounds of the split.
505+
let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) };
506+
507+
// SAFETY: We explicitly check for the correct number of elements,
508+
// do not let the reference outlive the slice,
509+
// and enforce exclusive mutability of the chunk by the split.
510+
Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init))
511+
}
512+
}
513+
514+
/// Returns the last element of the slice, or `None` if it is empty.
515+
///
516+
/// # Examples
517+
///
518+
/// ```
519+
/// #![feature(slice_first_last_chunk)]
520+
///
521+
/// let u = [10, 40, 30];
522+
/// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
523+
///
524+
/// let v: &[i32] = &[10];
525+
/// assert_eq!(None, v.last_chunk::<2>());
526+
///
527+
/// let w: &[i32] = &[];
528+
/// assert_eq!(Some(&[]), w.last_chunk::<0>());
529+
/// ```
530+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
531+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
532+
#[inline]
533+
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
534+
if self.len() < N {
535+
None
536+
} else {
537+
// SAFETY: We manually verified the bounds of the slice.
538+
// FIXME: Without const traits, we need this instead of `get_unchecked`.
539+
let last = unsafe { self.split_at_unchecked(self.len() - N).1 };
540+
541+
// SAFETY: We explicitly check for the correct number of elements,
542+
// and do not let the references outlive the slice.
543+
Some(unsafe { &*(last.as_ptr() as *const [T; N]) })
544+
}
545+
}
546+
547+
/// Returns a mutable pointer to the last item in the slice.
548+
///
549+
/// # Examples
550+
///
551+
/// ```
552+
/// #![feature(slice_first_last_chunk)]
553+
///
554+
/// let x = &mut [0, 1, 2];
555+
///
556+
/// if let Some(last) = x.last_chunk_mut::<2>() {
557+
/// last[0] = 10;
558+
/// last[1] = 20;
559+
/// }
560+
/// assert_eq!(x, &[0, 10, 20]);
561+
/// ```
562+
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
563+
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
564+
#[inline]
565+
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
566+
if self.len() < N {
567+
None
568+
} else {
569+
// SAFETY: We manually verified the bounds of the slice.
570+
// FIXME: Without const traits, we need this instead of `get_unchecked`.
571+
let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 };
572+
573+
// SAFETY: We explicitly check for the correct number of elements,
574+
// do not let the reference outlive the slice,
575+
// and require exclusive access to the entire slice to mutate the chunk.
576+
Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) })
577+
}
578+
}
579+
322580
/// Returns a reference to an element or subslice depending on the type of
323581
/// index.
324582
///

0 commit comments

Comments
 (0)