Разберите ограничение: почему Rust запрещает переместить значение, не реализующее Copy, через неизменяемую ссылку, даже если исходный владелец больше не будет использован?
Неизменяемая ссылка предоставляет доступ к значению, но не передаёт право владения им. Перемещение требует сделать исходное значение недействительным, тогда как владелец обязан сохранить его до конца своего времени жизни и корректно освободить.
Поэтому через &T нельзя переместить значение типа, не реализующего Copy. Можно прочитать его, скопировать побитово для Copy-типа или явно создать независимую копию через Clone.
Модель владения Rust предназначена для безопасного управления памятью без сборщика мусора. Она должна предотвращать use-after-free, двойное освобождение и обращение к уже перемещённому значению во время компиляции.
Заимствование позволяет временно передавать доступ к данным, не меняя владельца. Такое разделение прав делает возможным безопасное использование одного объекта несколькими участками программы без неявного копирования или ручного управления временем освобождения.
Пусть функция получила &T, где T — например, String. Ссылка указывает на объект, но объект по-прежнему принадлежит переменной-владельцу, которая позже должна уничтожить его.
Если бы функция могла переместить String через &String, владелец потерял бы значение, оставаясь формально ответственным за его освобождение. Это создало бы риск двойного освобождения. Кроме того, другая часть программы могла бы продолжить использовать ссылку после перемещения, что привело бы к обращению к недействительному объекту.
Операция разыменования &T даёт доступ к T, но не меняет режим владения. Для не-Copy типа попытка получить значение из &T трактуется как перемещение из заимствованного содержимого и запрещается компилятором.
Для типа, реализующего Copy, выражение через ссылку создаёт копию значения, поэтому исходный объект не теряет владение. Для String и других ресурсных типов требуется явный clone, который обычно выделяет память и копирует содержимое.
Изменяемая ссылка &mut T тоже не означает автоматическое право забрать значение. Если нужно заменить или извлечь содержимое, применяют операции, сохраняющие корректное состояние владельца: например, std::mem::replace, std::mem::take или Option::take. Они сначала оставляют на месте допустимое значение либо None, поэтому объект не остаётся логически неинициализированным.
Проверка последнего использования через NLL может сократить период заимствования, но не превращает ссылку во владельца. Даже если компилятор видит, что ссылка больше не будет использована, правило типов остаётся тем же: &T только заимствует данные.
Функция форматирования получает &String, но вызывающий код продолжает использовать исходную строку. Вариант с попыткой переместить строку эффективен по числу копирований, однако недопустим: функция не владеет объектом.
Первый рабочий вариант — вернуть String через clone. Он прост и сохраняет независимость результата, но требует выделения памяти и копирования всего содержимого. Второй вариант — вернуть &str, если результат должен быть только представлением исходных данных; это эффективнее, но результат нельзя использовать дольше исходной строки.
Выбор зависит от контракта. Для краткоживущего чтения предпочтительна ссылка, для независимого результата — clone; передача владения возможна только тогда, когда API действительно принимает владельца, а не заимствование. Такое решение предотвращает лишние копирования и одновременно сохраняет корректное время жизни данных.
Copy разрешает операцию через &T, а Clone сам по себе — нет?Copy означает, что значение можно неявно побитово дублировать без отдельной логики освобождения. При чтении *reference компилятор создаёт копию, поэтому исходное значение остаётся у владельца. Clone — явная операция, потенциально дорогая и произвольная по поведению, поэтому Rust не вызывает её автоматически при разыменовании.
Copy значение через &mut T?Прямое перемещение также запрещено: изменяемая ссылка не делает вызывающий код владельцем. Однако через &mut T можно безопасно заменить содержимое другим значением, например с помощью mem::replace, либо извлечь его из Option<T> через take. Эти операции оставляют объект в корректном состоянии и передают извлечённое значение вызывающему коду явно.
Безопасность определяется не только фактическим последним использованием ссылки, но и контрактом самого типа ссылки. &T гарантирует доступ к существующему значению на всём согласованном времени заимствования, а владелец сохраняет обязанность управлять его уничтожением. Разрешение выборочного перемещения разрушило бы эти гарантии и усложнило бы анализ кода, особенно при передаче ссылок в другие функции или сохранении их в структурах.