ProgrammazioneRust разработчик

Как в Rust устроена работа с коллекциями (например, Vec, HashMap)? В чем особенности их владения и когда необходимо клонировать элементы или коллекцию?

Supera i colloqui con l'assistente IA Hintsage

Ответ

В Rust стандартные коллекции (такие как Vec<T>, HashMap<K,V>, HashSet<T> и др.) реализуют принцип владения содержимым. Это означает, что коллекция владеет всеми объектами, которые она содержит, и отвечает за освобождение их памяти при удалении.

Например, элементы, помещенные во Vec<T>, перемещаются (move) в вектор, который далее владеет ими:

let s = String::from("hi"); let mut v = Vec::new(); v.push(s); // s теперь перемещён, не доступен

Если коллекция содержит копируемые значения, то они копируются; если перемещаемые — права на владение передаются. Клонировать элементы или коллекцию необходимо, когда нужно сохранить исходное значение в двух местах, например:

let name = String::from("Olga"); let mut v = Vec::new(); v.push(name.clone()); println!("{}", name); // name по-прежнему доступен, т.к. был клонирован

Вопрос с подвохом

Что происходит при вставке элемента в HashMap, если использовать перемещаемый тип данных (например, String) в качестве ключа? Будет ли доступен исходный ключ после передачи его в HashMap::insert()?

Часто дают неверный ответ: "Исходный ключ останется доступен после вставки".

На самом деле владение ключом и значением передается HashMap, и после вставки их нельзя использовать без предварительного клонирования:

let key = String::from("id"); let mut map = std::collections::HashMap::new(); map.insert(key, 42); // key больше недоступен

Попытка обращения к key вызовет ошибку компиляции.

Примеры реальных ошибок из-за незнания тонкостей темы


История Разработчик пытался использовать перемещённый после вставки в Vec объект и получил ошибку "value used here after move", не понимая, что Vector теперь владеет им.


История При работе с HashMap забыли клонировать строковые ключи, что привело к невозможности их использования в других местах, что потребовало значительного рефакторинга кода.


История В одном из проектов пытались вернуть ссылку на внутренний элемент вектора за пределами функции, забыв, что ссылка будет инвалидирована при изменении размера вектора. Это привело к появлению dangling references и крэшу во время выполнения (panic).