Как поменять местами элементы массива?

Я хочу поменять местами элементы slice data использование функции библиотеки, но она не работает из-за нескольких заимствований:

mem::swap(&mut data[i], &mut data[j]); //error

Это можно сделать вручную, как обычно:

let temp = data[i];
data[i] = data[j];
data[j] = temp;

но я не думаю, что использование этого кода каждый раз здорово. Есть ли другое решение для использования библиотеки swap для срезов обычно?

1 ответов


здесь swap метод на ломтики: data.swap(i, j).

исходный код не работает, потому что язык требует, чтобы &muts не псевдоним, то есть, если часть данных доступна через &mut, тогда не должно быть другого способа использовать эти данные. В общем, для последовательных индексов data[i], data[j] компилятор не может гарантировать, что i и j разные. Если они одинаковы, то индексирование относится к одной и той же памяти и поэтому &mut data[i] и &mut data[j] будет два указателя на одни и те же данные: незаконно!

.swap использует немного unsafe код внутри, не забудьте обработать i == j дело правильно, избегая сглаживания &mut указатели. Тем не менее, это не есть использовать unsafe, только для того, чтобы эта "примитивная" операция была высокопроизводительной (и я определенно мог представить будущие улучшения языка / библиотеки, которые уменьшают необходимость в небезопасном здесь, делая требование инварианты проще выразить), например, следующая безопасная реализация:

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]);
}

ключ здесь split_at_mut который разделяет срез на две непересекающиеся половины (это делается с помощью unsafe внутренне, но стандартная библиотека Rust построена на unsafe: язык предоставляет "примитивные" функции, а библиотеки строят остальные поверх них).