В чём разница между let и var в Swift?
let объявляет константу — после первого присваивания изменить нельзя; var объявляет переменную. Для value types let полностью замораживает содержимое, для reference types — фиксирует лишь ссылку.
let и var в Swift: константы и переменные
let объявляет неизменяемое значение (константу): после первого присваивания изменить его нельзя. var объявляет изменяемую переменную, значение которой можно перезаписывать сколько угодно раз.
let maxRetries = 3
// maxRetries = 5 // ошибка компиляции: cannot assign to value: 'maxRetries' is a 'let' constant
var currentRetry = 0
currentRetry += 1 // OK
Семантика для value types и reference types
Для value types (struct, enum, tuple) let полностью замораживает содержимое: нельзя мутировать ни поля, ни вызывать mutating методы.
struct Point {
var x: Double
var y: Double
mutating func move(dx: Double, dy: Double) {
x += dx; y += dy
}
}
let fixed = Point(x: 0, y: 0)
// fixed.x = 1 // ошибка
// fixed.move(dx: 1, dy: 0) // ошибка: cannot use mutating member on immutable value
var movable = Point(x: 0, y: 0)
movable.move(dx: 1, dy: 0) // OK
Для reference types (class) let фиксирует ссылку, но не сам объект. Поля объекта по-прежнему можно менять через методы, если сам класс это позволяет.
class Counter {
var count = 0
func increment() { count += 1 }
}
let counter = Counter()
counter.increment() // OK — ссылка не меняется, только поле
// counter = Counter() // ошибка — нельзя переназначить ссылку
Отложенная инициализация
let разрешает однократную отложенную инициализацию: можно объявить константу без начального значения и присвоить его позже — но ровно один раз, до первого чтения.
let result: Int
if Bool.random() {
result = 1
} else {
result = 2
}
print(result) // всегда инициализировано
Влияние на производительность и оптимизацию
Компилятор Swift активно использует let для оптимизаций: исключает ненужные копии, применяет inlining, убирает проверки изменяемости. Предпочитайте let по умолчанию и переходите на var только при необходимости.
let в замыканиях
В замыканиях let-значения захватываются по значению (для value types) или по ссылке (для reference types). var тоже захватывается по ссылке — изменения внутри замыкания влияют на оригинал.
var x = 10
let closure = { x += 1 } // захват x по ссылке
closure()
print(x) // 11
Подводные камни
- let на классе не делает объект иммутабельным — только ссылку. Разработчики часто ошибочно считают, что
let counter = Counter()защищает поля counter от изменений. - mutating в протоколах — если протокол требует
mutating func, а переменная объявлена черезlet, вызов невозможен даже если тип реализует протокол. - Захват var в замыкании — замыкание захватывает ящик переменной, а не копию; это классический источник bagов при работе с циклами и async-кодом.
- Computed properties не зависят от let/var — вы можете определить вычисляемое свойство в struct с
let-хранимыми полями; путаница возникает, когда новичок думает, что var нужен для computed. - Отложенная инициализация let строго однократна — попытка присвоить второе значение — ошибка компиляции, даже если первое ещё не было прочитано.
- Optional let —
let x: Int? = nilзаконно; значение nil не нарушает правило константы, но может вызвать путаницу при unwrap-ах.
Common mistakes
- Сводить «
letиvarв Swift» к синтаксису и не объяснять ARC. - Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии swift-1.
- Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.
What the interviewer is testing
- Формулирует точную модель для «
letиvarв Swift» и подтверждает ее корректным примером. - Умеет связать ответ с Swift Concurrency, тестированием и отладкой на устройстве.
- Называет ограничения подхода swift-1, включая производительность, память и сопровождение.