Разбираем, почему let у класса в Swift не запрещает изменение свойств, а у структуры — запрещает. Узнайте про указатели и структуры данных.
Вы когда-нибудь создавали константный экземпляр класса (let) в Swift, меняли его свойство — и это работало? А со структурой аналогичный код выдавал ошибку? Один и тот же ключ, разное поведение. В этой статье разберёмся, почему так происходит, и запомним главное правило: let для класса фиксирует указатель, а не данные.
Когда вы создаёте экземпляр класса и присваиваете его переменной, вы на самом деле держите указатель на данные, а не сами данные. Экземпляр класса живёт в памяти, а ваша переменная просто хранит адрес. Структуры, наоборот, хранят данные напрямую. Это ключевое различие, которое объясняет всё дальнейшее.
Вот что сбивает с толку новичков:
class Ninja {
var name: String
var powerLevel: Int
init(name: String, powerLevel: Int) {
self.name = name
self.powerLevel = powerLevel
}
}
let naruto = Ninja(name: "Naruto", powerLevel: 9000)
naruto.powerLevel = 9001 // ✅ РаботаетКак так? naruto объявлен через let! Ответ: let naruto означает, что указатель под именем naruto зафиксирован — он всегда будет указывать на этот конкретный экземпляр Ninja. Но данные, на которые он указывает, менять можно. Изменение naruto.powerLevel не перемещает указатель, а просто меняет значение по адресу.
Что же тогда запрещает let? Вот это:
let naruto = Ninja(name: "Naruto", powerLevel: 9000)
naruto = Ninja(name: "Sasuke", powerLevel: 8500) // ❌ ОшибкаНельзя заставить naruto указывать на другого ниндзя. Указатель зафиксирован. Но менять содержимое по текущему адресу — пожалуйста.
Теперь пример со структурой:
struct AnimeCharacter {
var name: String
var powerLevel: Int
}
let goku = AnimeCharacter(name: "Goku", powerLevel: 9001)
goku.powerLevel = 9002 // ❌ Не компилируетсяСтруктуры не используют указатели. let goku означает, что значение, хранящееся в goku, константно — и всё его содержимое тоже. Изменение любого поля структуры равносильно замене всей структуры целиком, а константу заменить нельзя. Если бы goku был объявлен через var, то Swift мог бы заменить всю структуру новой:
var goku = AnimeCharacter(name: "Goku", powerLevel: 9001)
goku.powerLevel = 9002 // ✅ РаботаетРассмотрим все варианты с классами:
let naruto = Ninja(...); // naruto.powerLevel = ... ❌; naruto = ... ❌ — указатель зафиксирован, свойство тоже.let naruto = Ninja(...); naruto.powerLevel = 9001 ✅; naruto = ... ❌ — указатель зафиксирован, но данные менять можно.var naruto = Ninja(...); // powerLevel = ... ❌; naruto = Ninja(...) ✅ — можно перенаправить указатель, но свойство не изменить.var naruto = Ninja(...); powerLevel = ... ✅; naruto = ... ✅ — полная гибкость.В отличие от структур, методы классов, меняющие свойства, не требуют ключевого слова mutating. Для структур mutating нужен, чтобы Swift мог проверить, можно ли вызывать метод на константной структуре. С классами проверка проще: достаточно посмотреть на объявление свойства (var или let).
Запомните: let для класса фиксирует указатель, а не данные. Вы можете менять переменные свойства константного класса, но не можете перенаправить указатель на другой экземпляр. let для структуры фиксирует всё, потому что структура — это и есть данные, без промежуточного указателя.
Попробуйте прямо сейчас: откройте Playground и поэкспериментируйте с четырьмя комбинациями. Это закрепит понимание на практике.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →