Выбор между
&self,
&mut self,
self и
mut self определяет поведение вашего API. Это не просто синтаксис - это контракт с пользователем. Рассмотрим на практике, как каждая форма влияет на дизайн.
Возьмем структуру
Cache - кэш в памяти с
TTL. Ее методы демонстрируют все формы
self.
&self - наблюдатель:
impl Cache {
fn is_expired(&self, key: &str) -> bool {
// Читает данные, не изменяя кэш
}
}
Метод работает с неизменяемой ссылкой. Можно вызывать параллельно из разных потоков.
&mut self - модификатор:
impl Cache {
fn insert(&mut self, key: String, value: String) {
// Требует эксклюзивного доступа
}
}
Изменяет внутреннее состояние. Компилятор гарантирует отсутствие других ссылок в этот момент.
self - потребитель:
impl Cache {
fn into_inner(self) -> HashMap {
// Забирает владение, освобождая ресурсы
}
}
После вызова оригинальный Cache перестаёт существовать. Идеально для финализаторов.
mut self - трансформер:
impl Cache {
fn with_ttl(mut self, ttl: Duration) -> Self {
self.ttl = ttl;
self // Возвращает изменённую копию
}
}
Позволяет использовать цепочки вызовов:
Cache::new().with_ttl(...).with_capacity(...)
Вывод:
Каждая форма
self устанавливает четкие правила взаимодействия с объектом. Использование
&self обеспечивает безопасное чтение без побочных эффектов. Через
&mut self реализуется контролируемое изменение с гарантиями исключительного доступа. Применение
self знаменует передачу ответственности и завершение жизненного цикла объекта. Наконец,
mut self позволяет осуществлять плавное преобразование, возвращая обновленную версию.
Осознанный выбор подходящей формы делает API не только интуитивно понятным, но и фундаментально надежным, демонстрируя, как
Rust превращает строгие ограничения системы владения в ключевые преимущества для разработчика.