При временном чтении через изменяемую ссылку, что происходит с возможностью последующей записи через исходную ссылку?
Rust создаёт временное повторное заимствование: изменяемая ссылка используется как источник неизменяемой ссылки на ограниченный срок. Пока это чтение активно, запись через исходную &mut запрещена; после последнего использования неизменяемой ссылки исходная изменяемая ссылка снова доступна.
Модель владения Rust предназначена для предотвращения гонок данных, использования освобождённой памяти и двойного освобождения без сборщика мусора. Для этого язык должен одновременно разрешать эффективную работу с данными и строго контролировать совместимость чтения и записи.
Повторное заимствование решает практическую проблему: функцию, которой достаточно прочитать значение, можно вызвать, не передавая ей владение и не отказываясь от дальнейшей записи через уже имеющуюся &mut.
Изменяемая ссылка предоставляет эксклюзивный доступ. Если во время записи оставить активной независимую ссылку для чтения, компилятор не смог бы гарантировать, что читатель не наблюдает значение в процессе изменения.
Поэтому Rust временно приостанавливает использование исходной &mut. Неверное представление, будто изменяемая ссылка просто копируется в &, приводит к ошибкам: копирование неограниченного доступа нарушило бы правило отсутствия одновременного чтения и записи.
Выражение вида &*mutable_ref не перемещает значение и не копирует саму изменяемую ссылку. Сначала разыменовывается &mut T, затем на полученное значение создаётся новая, более ограниченная ссылка &T — это и есть reborrow, или повторное заимствование.
На время существования &T исходная &mut T считается недоступной для операций, способных читать или изменять значение. После последнего фактического использования &T компилятор благодаря не лексическим временам жизни может завершить её заимствование ещё до конца блока, и запись через &mut T снова разрешается.
Вызов inspect получает временную неизменяемую ссылку на то же значение. writable нельзя использовать для записи внутри вызова и пока неизменяемая ссылка ещё нужна, но после вызова она снова может изменять число.
Повторное заимствование отличается от перемещения &mut T: при reborrow исходная ссылка не становится навсегда недоступной и не меняет владельца значения. Однако нельзя сохранить полученную &T на срок, пересекающийся с последующим использованием &mut для записи.
Представим обработчик, который получает &mut Request: сначала нужно передать запрос функции журналирования для чтения, затем изменить его статус.
Возможны три подхода. Клонирование запроса изолирует чтение, но может быть дорогим и потребовать реализации Clone. Передача владения невозможна или нежелательна, поскольку обработчик должен сохранить управление исходным запросом. Использование повторного заимствования не создаёт копию данных и точно ограничивает период чтения.
Поэтому выбирается вызов функции с временным &T, полученным из &mut T. После завершения журналирования исходная &mut Request снова используется для изменения статуса, а проверка компилятора гарантирует отсутствие пересечения чтения и записи.
Повторное заимствование копирует значение или ссылку?
Оно не копирует значение и не делает независимую копию исходной ссылки. Создаётся новый временный доступ к тому же объекту, а исходный &mut на этот период ограничивается правилами заимствования.
Можно ли изменять значение через исходную &mut, пока существует полученная &T?
Нет, если время использования &T пересекается с изменением. Иначе читатель мог бы получить значение до изменения, во время изменения или после него без гарантии согласованного состояния.
Важно, что область видимости переменной и фактическое время последнего использования могут различаться. При включённых не лексических временах жизни Rust обычно освобождает заимствование сразу после последнего чтения, а не обязательно в конце блока.
Чем повторное заимствование отличается от передачи &mut T в функцию?
При передаче &mut T функция обычно получает изменяемую ссылку на период вызова; это также может быть оформлено как временное повторное заимствование. После возврата исходная ссылка становится доступной, если функция не вернула из неё заимствование и не сохранила доступ способом, который продлевает срок займа.
Смысл операции остаётся тем же: владение объектом не меняется, но исходный доступ временно уступает место более узкому заимствованию с контролируемым временем жизни.