В 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).