Как изменится доступ к значению после передачи его владельца в другую переменную, если тип значения не реализует Copy?
После передачи владения исходная переменная становится недействительной: использовать её для чтения или изменения нельзя. Новая переменная становится единственным владельцем значения, поэтому Rust предотвращает двойное освобождение памяти и связанные с ним ошибки.
Владение в Rust решает классическую проблему системных языков: как управлять памятью без сборщика мусора и при этом исключить use-after-free, двойное освобождение и утечки из-за неясного владельца. Вместо ручного управления или фонового сборщика Rust проверяет правила владения во время компиляции.
Механизм move позволяет передавать ответственность за ресурс между переменными и функциями. Это делает время освобождения предсказуемым: когда последний владелец выходит из области видимости, Rust автоматически освобождает значение.
Если после передачи значения обе переменные продолжали бы считаться владельцами, они могли бы попытаться освободить один и тот же ресурс. Если же старое имя оставалось бы доступным без ограничений, оно могло бы обращаться к ресурсу после его освобождения новым владельцем.
Поэтому для типов, которые не реализуют Copy, присваивание или передача владельца обычно означает перемещение значения, а не его независимое копирование. Ошибка обнаруживается на этапе компиляции, до запуска программы.
При move само значение не обязательно физически копируется побайтно. Rust передаёт право владения новой переменной, а исходную переменную помечает как недоступную для дальнейшего использования.
Например:
String владеет данными в куче и не реализует Copy, поэтому first перемещается в second. После этого second освобождает строку при выходе из области видимости, а first не может освободить или использовать тот же ресурс повторно.
Типы, реализующие Copy, ведут себя иначе: при присваивании создаётся независимая копия значения, и исходная переменная остаётся доступной. Это применимо к небольшим типам, чья копия безопасна и проста, например к целым числам и булевым значениям.
Если владение нужно сохранить у вызывающего кода, используют заимствование: передают ссылку вместо самого значения. Заимствование не передаёт владельца, но подчиняется ограничениям времени жизни и правилам совместимости изменяемых и неизменяемых ссылок.
Компромисс таков: move повышает безопасность и делает управление ресурсами предсказуемым, но требует явно проектировать границы владения. Для больших данных это обычно выгоднее глубокого копирования, однако иногда приходится выбирать между передачей владения, заимствованием и явным клонированием.
В обработчике запроса нужно передать большое текстовое содержимое в функцию нормализации, а затем использовать результат в журналировании. Передача String по значению проста, но исходная переменная после вызова станет недоступной. Вызов с неизменяемой ссылкой сохраняет владение и не копирует буфер, но функция обязана вернуть отдельный результат, если нормализация создаёт новую строку.
Явное клонирование сохраняет доступ к исходному тексту и упрощает интерфейс, но может удвоить потребление памяти и время обработки. Практичный выбор — передать ссылку, если функции достаточно чтения, а новую строку создать только для нормализованного результата; это сохраняет владельца и избегает ненужной копии исходных данных.
Нет. Семантически перемещается владение, но конкретная реализация может передать или скопировать небольшой дескриптор значения, например указатель, длину и ёмкость String. Важно не физическое количество машинных инструкций, а то, что после move исходное имя нельзя использовать, а освобождать ресурс будет новый владелец.
Параметр функции получает владение тем же способом, что и другая переменная при присваивании. Если аргумент имеет тип, не реализующий Copy, он перемещается в параметр, и после вызова исходное имя обычно недоступно.
Если функции нужно только прочитать данные, параметр следует объявить как ссылку. Если функция должна окончательно забрать ресурс, передача по значению выражает это намерение и позволяет функции отвечать за его освобождение.
Да. Функция может принять значение во владение и вернуть его, передав владение обратно. Это безопасно, потому что в каждый момент существует один владелец, а компилятор проверяет, что значение не используется через старое имя между передачей и возвратом.
Такой стиль иногда неудобен из-за длинных цепочек передачи владения. Если ресурс не должен менять владельца, предпочтительнее заимствование; если нужна независимая копия, следует явно применить клонирование и учитывать его стоимость.