Skip to content

Commit 1dafe6d

Browse files
committed
Auto merge of #88540 - ibraheemdev:swap-unchecked, r=kennytm
add `slice::swap_unchecked` An unsafe version of `slice::swap` that does not do bounds checking.
2 parents 72d6606 + cf12732 commit 1dafe6d

File tree

2 files changed

+85
-13
lines changed

2 files changed

+85
-13
lines changed

Diff for: library/core/src/slice/mod.rs

+46-13
Original file line numberDiff line numberDiff line change
@@ -560,15 +560,52 @@ impl<T> [T] {
560560
#[stable(feature = "rust1", since = "1.0.0")]
561561
#[inline]
562562
pub fn swap(&mut self, a: usize, b: usize) {
563-
// Can't take two mutable loans from one vector, so instead use raw pointers.
564-
let pa = ptr::addr_of_mut!(self[a]);
565-
let pb = ptr::addr_of_mut!(self[b]);
566-
// SAFETY: `pa` and `pb` have been created from safe mutable references and refer
567-
// to elements in the slice and therefore are guaranteed to be valid and aligned.
568-
// Note that accessing the elements behind `a` and `b` is checked and will
569-
// panic when out of bounds.
563+
let _ = &self[a];
564+
let _ = &self[b];
565+
566+
// SAFETY: we just checked that both `a` and `b` are in bounds
567+
unsafe { self.swap_unchecked(a, b) }
568+
}
569+
570+
/// Swaps two elements in the slice, without doing bounds checking.
571+
///
572+
/// For a safe alternative see [`swap`].
573+
///
574+
/// # Arguments
575+
///
576+
/// * a - The index of the first element
577+
/// * b - The index of the second element
578+
///
579+
/// # Safety
580+
///
581+
/// Calling this method with an out-of-bounds index is *[undefined behavior]*.
582+
/// The caller has to ensure that `a < self.len()` and `b < self.len()`.
583+
///
584+
/// # Examples
585+
///
586+
/// ```
587+
/// #![feature(slice_swap_unchecked)]
588+
///
589+
/// let mut v = ["a", "b", "c", "d"];
590+
/// // SAFETY: we know that 1 and 3 are both indices of the slice
591+
/// unsafe { v.swap_unchecked(1, 3) };
592+
/// assert!(v == ["a", "d", "c", "b"]);
593+
/// ```
594+
///
595+
/// [`swap`]: slice::swap
596+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
597+
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
598+
pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
599+
#[cfg(debug_assertions)]
600+
{
601+
let _ = &self[a];
602+
let _ = &self[b];
603+
}
604+
605+
let ptr = self.as_mut_ptr();
606+
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
570607
unsafe {
571-
ptr::swap(pa, pb);
608+
ptr::swap(ptr.add(a), ptr.add(b));
572609
}
573610
}
574611

@@ -675,11 +712,7 @@ impl<T> [T] {
675712
// The resulting pointers `pa` and `pb` are therefore valid and
676713
// aligned, and can be read from and written to.
677714
unsafe {
678-
// Unsafe swap to avoid the bounds check in safe swap.
679-
let ptr = self.as_mut_ptr();
680-
let pa = ptr.add(i);
681-
let pb = ptr.add(ln - i - 1);
682-
ptr::swap(pa, pb);
715+
self.swap_unchecked(i, ln - i - 1);
683716
}
684717
i += 1;
685718
}

Diff for: library/core/tests/slice.rs

+39
Original file line numberDiff line numberDiff line change
@@ -2152,3 +2152,42 @@ fn test_slice_fill_with_uninit() {
21522152
let mut a = [MaybeUninit::<u8>::uninit(); 10];
21532153
a.fill(MaybeUninit::uninit());
21542154
}
2155+
2156+
#[test]
2157+
fn test_swap() {
2158+
let mut x = ["a", "b", "c", "d"];
2159+
x.swap(1, 3);
2160+
assert_eq!(x, ["a", "d", "c", "b"]);
2161+
x.swap(0, 3);
2162+
assert_eq!(x, ["b", "d", "c", "a"]);
2163+
}
2164+
2165+
mod swap_panics {
2166+
#[test]
2167+
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
2168+
fn index_a_equals_len() {
2169+
let mut x = ["a", "b", "c", "d"];
2170+
x.swap(4, 2);
2171+
}
2172+
2173+
#[test]
2174+
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
2175+
fn index_b_equals_len() {
2176+
let mut x = ["a", "b", "c", "d"];
2177+
x.swap(2, 4);
2178+
}
2179+
2180+
#[test]
2181+
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")]
2182+
fn index_a_greater_than_len() {
2183+
let mut x = ["a", "b", "c", "d"];
2184+
x.swap(5, 2);
2185+
}
2186+
2187+
#[test]
2188+
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")]
2189+
fn index_b_greater_than_len() {
2190+
let mut x = ["a", "b", "c", "d"];
2191+
x.swap(2, 5);
2192+
}
2193+
}

0 commit comments

Comments
 (0)