Avatar ·

В чем разница между let и var в Swift?

Привет! Я только начал изучать Swift и никак не могу понять разницу между `let` и `var`. Вроде бы оба используются для объявления переменных, но я заметил, что Xcode иногда ругается, если я использую не то слово. Например, я написал такой код: ```swift var name = "Иван" name = "Петр" // работает let age = 25 age = 30 // ошибка! Cannot assign to value: 'age' is a 'let' constant ``` Почему в первом случае всё нормально, а во втором ошибка? Я думал, что `let` и `var` — это одно и то же. Может быть, `let` вообще нельзя менять? Но зачем тогда он нужен? Ещё я заметил, что в примерах из интернета часто используют `let` для массивов и словарей, но потом добавляют в них элементы. Как это работает? Разве `let` не запрещает изменения? Объясните, пожалуйста, простыми словами, когда нужно использовать `let`, а когда `var`. И что будет, если я всегда буду писать `var` — это плохая практика? Заранее спасибо! <<>>

Ключевое различие: изменяемость

В Swift let и var отличаются главным образом тем, разрешают ли они изменять значение после первого присваивания. Это основа системы управления памятью и безопасности типов в языке.

  • let (константа) — значение нельзя изменить после инициализации. Один раз присвоили — и всё, дальше только чтение.
  • var (переменная) — значение можно менять сколько угодно раз.

Пример с объяснением

let pi = 3.14159
pi = 3.0  // Ошибка: pi — константа

var counter = 0
counter += 1  // Работает: counter — переменная

А как же массивы и словари?

Тут есть важный нюанс. let запрещает замену самого значения, но не запрещает изменение содержимого, если тип является ссылочным (класс) или имеет мутирующие методы (структуры с методами, помеченными mutating).

Пример со словарём:

let capitals = ["Россия": "Москва", "Франция": "Париж"]
capitals["Германия"] = "Берлин"  // Работает! Добавляем новую пару
capitals = ["Италия": "Рим"]     // Ошибка! Нельзя заменить сам словарь

Почему так? Потому что capitals — это константа, указывающая на область памяти. Саму область памяти менять нельзя (нельзя заставить capitals указывать на другой словарь), но содержимое этой области (добавление/удаление элементов) — можно, если тип это поддерживает.

Когда что использовать?

  • Всегда начинайте с let. Если компилятор не ругается — оставляйте let. Это делает код безопаснее и понятнее.
  • Используйте var только если значение действительно должно меняться. Например, счётчик циклов, аккумулятор суммы, состояние UI.
  • Для IBOutlet и подобных вещей, которые инициализируются позже, используйте var (или let с отложенной инициализацией).

А что будет, если всегда писать var?

Код будет работать, но это плохая практика по нескольким причинам:

  • Вы теряете информацию о намерениях: let явно говорит "это значение не изменится", var — "будьте осторожны, оно может поменяться".
  • Компилятор не сможет оптимизировать код (константы легче оптимизировать).
  • Вы рискуете случайно изменить значение, которое не должно меняться, и получить трудноуловимую ошибку.

Дополнительно: let в замыканиях

Если вы используете let внутри замыкания, вы захватываете значение, а не ссылку. Это важно для избежания циклических ссылок.

var x = 10
let closure = { [x] in print(x) }  // Захватываем значение 10
x = 20
closure()  // Выведет 10, а не 20

Итог: let — это константа, var — переменная. Используйте let везде, где возможно. Это сделает ваш код чище, безопаснее и производительнее.

<<>>

Практический совет: "let by default"

В Swift сообществе есть золотое правило: всегда используйте let, пока компилятор не скажет иначе. Это не просто стиль, а способ избежать целого класса ошибок.

Представьте, что вы пишете функцию:

func calculateTotal(price: Double, tax: Double) -> Double {
    let total = price + price * tax  // total не должен меняться
    // ... много кода ...
    // если бы total был var, вы могли бы случайно его изменить
    return total
}

Используя let, вы гарантируете, что total останется именно тем, что вы вычислили. Если позже кто-то (или вы сами) попытается его изменить, компилятор сразу выдаст ошибку.

Ещё один пример — работа с опционалами:

let user: String? = getUserName()
if let name = user {  // name — константа, её нельзя изменить
    print("Привет, \(name)")
}

Здесь if let создаёт временную константу name, и вы не сможете случайно её перезаписать внутри блока.

Запомните: var — это исключение, а не правило. Если вы пишете var, спросите себя: "Действительно ли это значение будет меняться?" Если нет — замените на let. Ваш будущий я и коллеги скажут спасибо.