Как Cow откладывает клонирование заимствованных данных до первой мутации?
Cow хранит данные либо как заимствованные (Borrowed), либо как владеющие (Owned). Пока значение только читают, копирования нет; при вызове операции, требующей изменяемого доступа, Cow клонирует заимствованные данные ровно в этот момент и продолжает работу с собственной копией.
Подход copy-on-write применяют, чтобы не создавать копию данных заранее, если большинство операций не изменяет их. В Rust это особенно важно из-за строгих правил владения: изменять заимствованные данные нельзя, поэтому перед первой мутацией требуется получить отдельное владеющее значение.
std::borrow::Cow объединяет два представления в одном типе: ссылку на исходные данные и их owned-копию. Это позволяет функции принимать как временно заимствованные данные, так и уже готовый владеющий буфер.
Функция может в большинстве случаев вернуть исходную строку без изменений, но иногда должна её модифицировать. Без отложенного копирования пришлось бы либо всегда выделять новую строку, либо проектировать отдельные пути для заимствованных и владеющих данных.
Преждевременное копирование увеличивает потребление памяти и время выполнения. Отсутствие копирования при необходимости изменения было бы небезопасным: функция могла бы изменить память, которой не владеет или которая одновременно используется другим кодом.
Cow<'a, B> имеет концептуально два состояния: Borrowed(&'a B) и Owned(B::Owned). Для строк B обычно равен str, а владеющий типом является String.
Метод to_mut() возвращает изменяемую ссылку на владеющие данные. Если Cow уже находится в состоянии Owned, он не клонирует значение. Если состояние Borrowed, метод сначала создаёт owned-копию через ToOwned, заменяет внутреннее состояние и только затем возвращает изменяемую ссылку.
В первом пути возвращается ссылка на исходную строку, поэтому выделения памяти нет. Во втором to_mut() клонирует input в String, после чего префикс добавляется безопасно.
Cow не гарантирует отсутствия копирования вообще: любое изменение заимствованного значения требует клонирования. Кроме того, многократное обращение к to_mut() после первого перехода в Owned новых копий не создаёт.
Важен и срок жизни 'a: заимствованный вариант не может жить дольше исходных данных. Если вызывающей стороне требуется независимый результат, можно получить владеющее значение через into_owned(), но для заимствованного варианта это потребует копирования.
Предположим, HTTP-сервис нормализует путь запроса: почти все пути уже имеют нужный префикс, но часть запросов требует его добавить. Возвращать всегда новый String просто, однако это создаёт лишние аллокации на наиболее частом пути.
Вариант с безусловным String имеет предсказуемое поведение, но платит памятью и копированием для каждого запроса. Вариант с двумя отдельными функциями уменьшает расходы, но усложняет API и логику вызывающего кода.
Cow<str> позволяет оставить единый интерфейс: неизменённый путь возвращается как заимствованный, а изменённый материализуется в String только при необходимости. Компромисс состоит в дополнительной проверке состояния и потенциальной непредсказуемой аллокации на пути первой мутации; для сценария «обычно читаем, иногда изменяем» это обычно оправдано.
Cow данные при создании из ссылки?Нет. Создание Cow::Borrowed сохраняет ссылку и не копирует содержимое. Копирование происходит только при переходе к изменяемому owned-представлению, например через to_mut(), либо при явном получении независимого значения через into_owned().
Cow изначально создан как Owned?to_mut() вернёт изменяемую ссылку на уже принадлежащие Cow данные без дополнительного клонирования. Поэтому передача готового String в Cow может сохранить владение и избежать копирования даже при последующей модификации.
Cow не позволяет изменить заимствованный вариант напрямую?Заимствованная ссылка не даёт владельцу права единолично изменять данные. Исходное значение может использоваться другими ссылками или принадлежать вызывающему коду. Переход в Owned создаёт собственное хранилище, после чего изменение выполняется через уникальный изменяемый доступ и соответствует правилам безопасной памяти Rust.