There's a swap
method on slices: data.swap(i, j)
.
The original code doesn't work because the language requires that &mut
s do not alias, that is, if a piece of data is accessible via an &mut
, then there must be no other way to use that data. In general, for successive indexes data[i]
, data[j]
, the compiler cannot guarantee that i
and j
are different. If they are the same then the indexing is referring to the same memory and so &mut data[i]
and &mut data[j]
would be two pointers to the same data: illegal!
.swap
uses a bit of unsafe
code internally, being sure to handle the i == j
case correctly, avoiding aliasing &mut
pointers. That said, it doesn't have to use unsafe
, it is only to ensure this "primitive" operation has high-performance (and I could definitely imagine future language/library improvements that reduce the need for unsafe here by making the require invariants easier to express), e.g. the following is a safe implementation:
use std::cmp::Ordering;
use std::mem;
fn swap<T>(x: &mut [T], i: usize, j: usize) {
let (lo, hi) = match i.cmp(&j) {
// no swapping necessary
Ordering::Equal => return,
// get the smallest and largest of the two indices
Ordering::Less => (i, j),
Ordering::Greater => (j, i),
};
let (init, tail) = x.split_at_mut(hi);
mem::swap(&mut init[lo], &mut tail[0]);
}
The key here is split_at_mut
which separates the slice into two disjoint halves (this is done using unsafe
internally, but Rust's standard library is built on unsafe
: the language provides "primitive" features and the libraries build the rest on top of them).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…