При передаче уже имеющейся изменяемой ссылки в функцию с параметром того же типа почему исходная ссылка обычно остаётся доступной после вызова?
Rust обычно выполняет повторное изменяемое заимствование: вместо перемещения исходной &mut T в функцию создаётся временная ссылка &mut *исходная_ссылка. Это заимствование действует в пределах вызова, поэтому после его завершения исходную ссылку можно снова использовать.
Такое поведение возможно, когда контекст вызова ожидает именно &mut T. Сам тип &mut T не является Copy, поэтому при передаче в обобщённый параметр или при сохранении значения перемещение всё же может произойти.
Модель владения Rust должна одновременно предотвращать гонки данных и позволять последовательно передавать один изменяемый объект нескольким операциям. Простое перемещение каждой &mut T сделало бы такой код неудобным: после одного вызова исходный доступ пришлось бы создавать заново.
Повторное заимствование сохраняет правило единственного активного изменяемого доступа, но ограничивает его коротким участком использования. Это компромисс между безопасностью алиасинга и практичностью композиции функций.
Изменяемая ссылка предоставляет эксклюзивный доступ к значению. Если бы несколько независимых &mut T могли одновременно существовать и использоваться, изменения через одну ссылку могли бы конфликтовать с чтением или изменением через другую.
При этом вызов функции часто должен временно получить тот же эксклюзивный доступ, а затем вернуть его владельцу ссылки. Ошибочное понимание механизма приводит либо к ожиданию ошибки компиляции там, где код корректен, либо к неожиданной недоступности ссылки после вызова в случаях, когда она действительно была перемещена.
В вызове функции с ожидаемым параметром &mut T выражение типа &mut T обычно трактуется как источник для нового заимствования. Концептуально передача ссылки эквивалентна созданию ссылки на значение, расположенное за исходной ссылкой: &mut *ссылка.
Временная повторная ссылка получает срок жизни, достаточный для вызова. Пока этот вызов продолжается, исходную ссылку нельзя использовать другим способом, потому что её исключительный доступ временно передан повторному заимствованию. После завершения вызова ограничение заканчивается.
В примере reference не перемещается в increment: функция получает временный доступ к тому же i32. Поэтому после вызова ссылка снова доступна.
Повторное заимствование не означает, что &mut T стал копируемым. Если параметр принимает произвольный тип, например обобщённый T, компилятор не обязан создавать &mut *reference: в таком контексте ссылка может быть перемещена как обычное значение. Аналогично, передача ссылки в структуру или возврат её из функции обычно означает передачу владения самой ссылкой, если специальное заимствование не выведено из контекста.
Важна также продолжительность последующего использования. Если после вызова исходная ссылка больше не используется, компилятор может разрешить более короткое или одноразовое заимствование; если она используется снова, повторное заимствование должно завершиться до этого использования. Так проверяются одновременно эксклюзивность доступа и корректность времени жизни.
Представим функцию обработки буфера, которая временно изменяет его через &mut [u8], после чего вызывающий код продолжает работу с тем же буфером. Вариант с передачей владения контейнером потребовал бы вернуть контейнер обратно и создал бы лишнюю сложность в интерфейсе. Вариант с несколькими независимыми изменяемыми ссылками был бы небезопасен.
Выбранный вариант — передавать одну &mut и полагаться на повторное заимствование при каждом последовательном вызове. Каждый вызов получает эксклюзивный доступ только на необходимый срок, а исходная ссылка остаётся доступной между вызовами. Это не требует копирования данных и сохраняет статическую проверку отсутствия конфликтующего доступа.
Если же ссылку нужно сохранить внутри другого объекта или передать в функцию, которая принимает параметр без ожидаемого типа &mut T, нужно явно учитывать возможность перемещения. В такой ситуации интерфейс следует проектировать как заимствующий, если получателю не требуется владеть самой ссылкой.
Ответ: &mut T не реализует Copy, потому что копирование создало бы два независимых изменяемых доступа к одному значению. При повторном заимствовании исходная ссылка остаётся единственным владельцем доступа, а новая ссылка лишь временно использует его. После окончания нового заимствования исходная ссылка снова может быть активна.
Ответ: у обобщённой функции с параметром типа T нет требования создать заимствование. Для неё &mut T — обычное значение типа T, которое может быть передано по владению. После такого вызова исходное имя обычно нельзя использовать, поскольку значение ссылки перемещено. Автоматическое повторное заимствование связано с ожидаемым ссылочным типом параметра, а не с самим фактом вызова функции.
Ответ: да, если полученная ссылка возвращается или сохраняется, но тогда её срок жизни должен быть согласован с исходной ссылкой, а исходная ссылка остаётся недоступной до окончания нового заимствования. Для обычного вызова без возврата ссылки компилятор обычно ограничивает повторное заимствование телом вызова, что позволяет использовать исходную ссылку после него. Решение определяется реальным последующим использованием, а не только областью видимости переменной.