Как скопировать последний элемент из фрагмента - PullRequest
0 голосов
/ 27 августа 2018

Я новичок в Rust и пытаюсь освоить идиоматический способ работы с программой проверки заимствований.

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

fn last_element<T>(list: &[T]) -> T {
    list[list.len() - 1]
}

fn main() {
    let mut slice = [1, 2, 3, 4, 5];
    let x = last_element(&slice);
    println!("{}", x);

    // I want to be able to mutate slice after extracting last element
    slice[2] = 17;
}

Ошибка: cannot move out of type [T] , a non-copy slice. Я вижу, что один из обходных путей состоит в том, чтобы функция возвращала ссылку:

fn last_element<T>(list: &[T]) -> &T {
    &list[list.len() - 1]
}

fn main() {
    let mut slice = [1, 2, 3, 4, 5];
    let x = last_element(&slice);
    println!("{}", x);
    // I want to be able to mutate slice after extracting last element
    slice[2] = 17;
}

Но тогда я получаю ошибку для slice[2] = 17, потому что срез был заимствован при назначении x. Я хочу иметь возможность мутировать после вызова last_element. Единственный обходной путь, который я нашел, состоял в разыменовании x, которое, я считаю, потребляет заем:

fn last_element<T>(list: &[T]) -> &T {
    &list[list.len() - 1]
}

fn main() {
    let mut slice = [1, 2, 3, 4, 5];
    let x = *last_element(&slice);
    println!("{}", x);
    // I want to be able to mutate slice after extracting last element
    slice[2] = 17;
}

Является ли это наиболее идиоматическим способом достижения цели, заключающейся в возможности получить последний элемент среза и все еще мутировать срез после этого? Или я никогда не буду делать мутации потом, если я пишу хороший код?

1 Ответ

0 голосов
/ 27 августа 2018

Если вы работаете с простыми типами, такими как i32 и другими небольшими структурами фиксированного размера, эти типы обычно реализуют Copy. Эта черта является маркером, который указывает компилятору копировать значения в памяти в ситуациях, когда они иначе были бы перемещены. Фактически, это то, что означает сообщение об ошибке, когда оно ссылается на non-copy slice: если элементы не реализуют Copy, тогда ему придется их перемещать, и это оставит срез в недопустимом состоянии, что не является допускается.

Если вы ограничите свою функцию ожиданием только Copy типов, тогда она с радостью скопирует для вас эти значения:

fn last_element<T: Copy>(list: &[T]) -> T {
    list[list.len() - 1]
}

Если тип ваших элементов может быть более сложным и не реализует Copy, тогда вы можете ограничить функцию шире, любым типом, который реализует Clone, а затем вызвать clone() для элемента перед возвращается:

fn last_element<T: Clone>(list: &[T]) -> T {
    list[list.len() - 1].clone()
}

Клонирование, как правило, более сложная операция, чем копирование, так как оно обычно реализуется для каждого поля в коде Rust, тогда как Copy - это операция более низкого уровня в необработанной памяти.

Последний вариант - просто вернуть ссылку на последний элемент в первую очередь. Затем вызывающая функция может решить, что с ней делать. Если это тип Copy (например, числа), то разыменование скопирует его:

fn last_element<T>(list: &[T]) -> &T {
    &list[list.len() - 1]
}

fn main() {
    let mut slice = [1, 2, 3, 4, 5];
    let x = *last_element(&slice);
}

Если бы элементы были Clone, но не Copy, тогда вместо разыменования вы можете явно clone это вместо этого:

let x = last_element(&slice).clone();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...