Skip to content

Commit

Permalink
Auto merge of #88540 - ibraheemdev:swap-unchecked, r=kennytm
Browse files Browse the repository at this point in the history
add `slice::swap_unchecked`

An unsafe version of `slice::swap` that does not do bounds checking.
  • Loading branch information
bors committed Oct 15, 2021
2 parents 72d6606 + cf12732 commit 1dafe6d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 13 deletions.
59 changes: 46 additions & 13 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,15 +560,52 @@ impl<T> [T] {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn swap(&mut self, a: usize, b: usize) {
// Can't take two mutable loans from one vector, so instead use raw pointers.
let pa = ptr::addr_of_mut!(self[a]);
let pb = ptr::addr_of_mut!(self[b]);
// SAFETY: `pa` and `pb` have been created from safe mutable references and refer
// to elements in the slice and therefore are guaranteed to be valid and aligned.
// Note that accessing the elements behind `a` and `b` is checked and will
// panic when out of bounds.
let _ = &self[a];
let _ = &self[b];

// SAFETY: we just checked that both `a` and `b` are in bounds
unsafe { self.swap_unchecked(a, b) }
}

/// Swaps two elements in the slice, without doing bounds checking.
///
/// For a safe alternative see [`swap`].
///
/// # Arguments
///
/// * a - The index of the first element
/// * b - The index of the second element
///
/// # Safety
///
/// Calling this method with an out-of-bounds index is *[undefined behavior]*.
/// The caller has to ensure that `a < self.len()` and `b < self.len()`.
///
/// # Examples
///
/// ```
/// #![feature(slice_swap_unchecked)]
///
/// let mut v = ["a", "b", "c", "d"];
/// // SAFETY: we know that 1 and 3 are both indices of the slice
/// unsafe { v.swap_unchecked(1, 3) };
/// assert!(v == ["a", "d", "c", "b"]);
/// ```
///
/// [`swap`]: slice::swap
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
#[cfg(debug_assertions)]
{
let _ = &self[a];
let _ = &self[b];
}

let ptr = self.as_mut_ptr();
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
unsafe {
ptr::swap(pa, pb);
ptr::swap(ptr.add(a), ptr.add(b));
}
}

Expand Down Expand Up @@ -675,11 +712,7 @@ impl<T> [T] {
// The resulting pointers `pa` and `pb` are therefore valid and
// aligned, and can be read from and written to.
unsafe {
// Unsafe swap to avoid the bounds check in safe swap.
let ptr = self.as_mut_ptr();
let pa = ptr.add(i);
let pb = ptr.add(ln - i - 1);
ptr::swap(pa, pb);
self.swap_unchecked(i, ln - i - 1);
}
i += 1;
}
Expand Down
39 changes: 39 additions & 0 deletions library/core/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2152,3 +2152,42 @@ fn test_slice_fill_with_uninit() {
let mut a = [MaybeUninit::<u8>::uninit(); 10];
a.fill(MaybeUninit::uninit());
}

#[test]
fn test_swap() {
let mut x = ["a", "b", "c", "d"];
x.swap(1, 3);
assert_eq!(x, ["a", "d", "c", "b"]);
x.swap(0, 3);
assert_eq!(x, ["b", "d", "c", "a"]);
}

mod swap_panics {
#[test]
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
fn index_a_equals_len() {
let mut x = ["a", "b", "c", "d"];
x.swap(4, 2);
}

#[test]
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")]
fn index_b_equals_len() {
let mut x = ["a", "b", "c", "d"];
x.swap(2, 4);
}

#[test]
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")]
fn index_a_greater_than_len() {
let mut x = ["a", "b", "c", "d"];
x.swap(5, 2);
}

#[test]
#[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")]
fn index_b_greater_than_len() {
let mut x = ["a", "b", "c", "d"];
x.swap(2, 5);
}
}

0 comments on commit 1dafe6d

Please sign in to comment.