Как выбор получателя метода по владению или по заимствованию определяет, останется ли объект доступен после вызова?
Если метод принимает получателя по владению, вызов перемещает объект в метод, поэтому прежнее имя больше нельзя использовать. Получатель &self только заимствует объект, а &mut self временно получает исключительный доступ; после завершения такого вызова владелец остаётся доступен.
Владение в Rust решает задачу безопасного управления ресурсами без сборщика мусора: у значения должен быть однозначный владелец, а освобождение происходит предсказуемо. Правила методов позволяют выразить в типе API, сохраняет ли операция объект или поглощает его.
Такой подход предотвращает использование объекта после передачи владения и двойное освобождение памяти. При этом обычные операции чтения и изменения не требуют перемещения самого значения.
Метод, принимающий self, может поглощать объект: например, извлекать его внутренний ресурс или превращать его в другой тип. После такого вызова исходная переменная недоступна, потому что объект уже принадлежит методу.
Методы с &self и &mut self ведут себя иначе. Они не меняют владельца, но накладывают ограничения на доступ во время вызова: первый разрешает совместное чтение, второй — эксклюзивное чтение и изменение.
У метода есть специальный параметр-получатель:
self принимает значение целиком и перемещает его в тело метода;&self создаёт неизменяемое заимствование;&mut self создаёт изменяемое заимствование.Вызов len временно заимствует buffer, поэтому после него переменная всё ещё доступна. Вызов discard передаёт buffer по владению; после него использовать это имя нельзя. Метод может не использовать поля, но сам факт параметра self означает передачу владения.
Rust иногда автоматически добавляет заимствование при вызове метода с &self или &mut self. Это не происходит для метода с self: автоматическое заимствование не может превратить операцию передачи владения в безопасную передачу ссылки.
Метод с &mut self требует, чтобы объект был доступен для изменения и чтобы на время вызова не существовало конфликтующих заимствований. Метод с self требует доступного владельца и завершает его существование в прежнем месте после перемещения.
Потребляющие методы полезны для преобразований и финализации объекта, но уменьшают повторное использование значения. Методы с заимствованием гибче, однако не могут безопасно пережить владельца и не должны извлекать владение из объекта через обычную ссылку.
Представим тип конфигурации, который после сборки должен стать неизменяемым объектом запуска. Можно реализовать сборку как метод с &mut self: это позволяет повторно использовать тот же экземпляр, но оставляет промежуточное состояние доступным и требует отдельно контролировать, завершена ли настройка.
Альтернатива — метод с self, возвращающий готовый объект. Такой API гарантирует, что исходную конфигурацию нельзя случайно изменить после сборки. Минус — переменная конфигурации поглощается, поэтому для повторной сборки нужно создать новый экземпляр или явно клонировать данные.
Для одноразового перехода из промежуточного состояния в финальное обычно выбирают self: потеря прежнего имени становится частью гарантии корректного жизненного цикла. Для многократных независимых изменений выбирают &mut self.
Можно ли вызвать метод с self, если объект заимствован?
Нет. Заимствованный объект нельзя переместить через ссылку, поскольку это могло бы оставить ссылку указывающей на значение, которого больше нет на прежнем месте. Сначала все заимствования должны закончиться, а затем нужно передать самого владельца.
Почему метод с &mut self может изменить поля, хотя не владеет объектом?
Изменяемое заимствование временно предоставляет эксклюзивный доступ к объекту. Владелец на этот период не используется напрямую, поэтому компилятор сохраняет гарантию отсутствия одновременного конфликтующего доступа. После завершения заимствования владелец снова может обращаться к изменённому объекту.
Всегда ли вызов метода с self физически перемещает все байты объекта?
Не обязательно. Перемещение — семантическая передача владения; для некоторых типов компилятор может оптимизировать физическое копирование или перестановку данных. Однако правила доступа остаются теми же: прежнее имя считается недоступным, а метод становится владельцем значения.