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