Какое правило заимствования гарантирует отсутствие конфликта между чтением и записью данных?
В один момент времени допускается либо любое число неизменяемых заимствований, либо ровно одно изменяемое заимствование. Эти варианты не могут пересекаться по времени: пока существует активная ссылка для чтения, запись запрещена, а изменяемая ссылка должна быть единственной.
Подход Rust решает классические проблемы системного программирования: висячие указатели, use-after-free, двойное освобождение и гонки данных. В языках с ручным управлением памятью такие ошибки часто обнаруживаются только во время выполнения.
Rust переносит значительную часть проверок на этап компиляции. Владение и заимствование позволяют сохранить предсказуемую производительность без обязательного сборщика мусора, одновременно формально ограничивая совместный доступ к памяти.
Если один участок программы читает данные, а другой одновременно изменяет их, читатель может увидеть несогласованное состояние. Дополнительный риск возникает, когда изменяется структура данных и прежняя ссылка на её элемент становится недействительной.
Разрешение одновременно читать и менять один объект потребовало бы синхронизации или иной гарантии безопасности. Поэтому компилятор запрещает пересекающиеся несовместимые заимствования ещё до запуска программы.
Неизменяемая ссылка &T разрешает чтение, но не изменение объекта через эту ссылку. Несколько таких ссылок могут существовать одновременно, поскольку они не создают конфликтов записи.
Изменяемая ссылка &mut T предоставляет эксклюзивный доступ: пока она активна, нельзя создать другую ссылку к тому же объекту, которая пересекается с ней по времени. Это правило предотвращает одновременный доступ к одним данным с несовместимыми намерениями.
Компилятор анализирует время жизни ссылок. Современный анализ учитывает последнее фактическое использование ссылки, поэтому заимствование может завершиться раньше конца внешнего блока.
В примере срез prefix существует только внутри вложенного блока. После завершения его использования изменяемая операция допустима. Если попытаться использовать prefix после push, компилятор отклонит программу: неизменяемое заимствование пересекается с изменяемым.
Правило относится к пересекающимся периодам доступа, а не просто к наличию переменной-ссылки в тексте программы. Когда обычная статическая проверка неудобна, Rust допускает внутреннюю изменяемость через типы вроде RefCell<T> или Mutex<T>, но тогда часть проверок переносится во время выполнения, а у RefCell нарушение правил приводит к панике.
В функции обработки текста требовалось сначала получить срез заголовка, затем добавить маркер в исходную строку. Прямое удержание среза во время изменения строки не проходит проверку: изменение может изменить расположение данных, на которые срез ссылается.
Рассматривались три варианта:
Выбран первый вариант: срез используется и уничтожается до изменения строки. Он сохраняет нулевую стоимость проверок во время выполнения и явно показывает безопасный порядок операций.
1. Можно ли создать несколько изменяемых заимствований последовательно?
Да, если периоды их использования не пересекаются. Первое &mut должно перестать использоваться до создания или использования второго. Компилятор способен распознать это по фактическим последним обращениям к ссылке, поэтому последовательные операции часто допустимы без явных блоков.
2. Запрещает ли правило заимствования любое изменение объекта, если существует неизменяемая ссылка?
Нет, запрещено именно конфликтующее изменение, пока неизменяемая ссылка активна. После последнего использования &T изменение может стать допустимым. Кроме того, типы с внутренней изменяемостью могут менять внутреннее состояние через &T, но безопасность этого механизма обеспечивается специальной реализацией типа, например динамическими проверками RefCell.
3. Почему компилятор не разрешает одновременные ссылки, если программист уверен, что они не конфликтуют?
Потому что компилятор должен опираться на проверяемые правила, а не на намерение программиста. Если статический анализ не может доказать отсутствие пересечения или нарушения времени жизни, программа отклоняется; обычно проблему решают сокращением области заимствования, перестановкой операций или явным копированием небольшого значения.