let, var и const: три способа объявить значение

Урок о том, как в Nim правильно выбрать способ хранить данные: let, var или const.

Связывание (binding) — это то, что в Nim происходит, когда вы пишете let x = 5 или var y = 10: имени присваивается значение, и дальше язык следит, можно ли это значение менять.

Зачем в языке сразу три ключевых слова

В школе на уроках алгебры вы привыкли, что x = 5 — это просто присваивание, и всё. В программировании всё чуть сложнее: важно не только какое значение хранится, но и можно ли его поменять потом. Nim различает три ситуации, и для каждой у него своё ключевое слово: let, var и const.

Если вы писали на Python, там такого деления нет — любая переменная в нём в любой момент может получить новое значение. Nim в этом смысле строже, и это осознанный выбор создателей языка: чем меньше в программе значений, которые могут неожиданно измениться, тем меньше в ней ошибок. Компилятор Nim проверяет эти правила ещё ДО запуска программы, поэтому многие баги вы увидите не во время выполнения, а сразу, при попытке собрать код.

let — значение, которое нельзя переприсвоить

let — это способ сказать: «я вычисляю это значение один раз, и дальше оно не должно меняться». Это самый частый случай в реальном коде: большинство значений, которые вы вычисляете, действительно не нужно потом менять — вы просто используете их дальше.

let name = "Аня"
let age = 16

echo name, " учится в ", age, " лет"

Вывод:

Аня учится в 16 лет

Если попробовать присвоить name новое значение ещё раз, компилятор откажется собирать программу:

let name = "Аня"
name = "Боря"  # ошибка компиляции

Вывод:

Error: 'name' cannot be assigned to

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

var — обычная изменяемая переменная

Когда значение по смыслу задачи должно меняться — счётчик в цикле, накопленная сумма, состояние игры — используется var. Это ближе всего к тому, что вы привыкли называть «переменной» в других языках.

var score = 0
score = score + 10
score = score + 5

echo "Итоговый счёт: ", score

Вывод:

Итоговый счёт: 15

Здесь всё работает ожидаемо: значению score можно присваивать новое значение сколько угодно раз. Хорошее правило: используйте var, только когда переменная ДЕЙСТВИТЕЛЬНО меняется дальше по коду. Если вы объявили var, но ни разу не переприсвоили значение — по смыслу это должен был быть let, и компилятор Nim обычно даже предупреждает об этом.

const — значение, известное ещё до запуска программы

const — самый строгий вариант. Это тоже неизменяемое значение, как let, но с дополнительным требованием: оно должно быть известно уже на этапе компиляции, то есть вычислено из других констант или литералов, а не из того, что программа узнаёт во время своей работы (например, не из ввода пользователя).

const maxPlayers = 4
const gameTitle = "Космобитва"
const secondsInMinute = 60
const secondsInHour = secondsInMinute * 60

echo gameTitle, ": до ", maxPlayers, " игроков"
echo "В часе секунд: ", secondsInHour

Вывод:

Космобитва: до 4 игроков
В часе секунд: 3600

Обратите внимание: secondsInHour вычисляется через умножение других констант — это разрешено, потому что результат всё равно можно посчитать заранее, ещё до того, как программа запустится. А вот так не получится:

import std/times

const startedAt = now()  # ошибка: время выполнения, не компиляции

Вывод:

Error: cannot evaluate at compile time: now()

Текущее время компилятор узнать заранее не может — оно появляется только при запуске программы, поэтому для него подошёл бы let, а не const.

Как выбирать между ними на практике

Простое правило, которым реально пользуются на практике: начинайте с let. Переключайтесь на var только тогда, когда компилятор явно потребует этого — то есть когда вы попытаетесь присвоить значение повторно. А const берите для настроек и «магических чисел», которые не зависят от хода программы: размеров, лимитов, названий, математических констант.

Ключевое словоМожно менять?Когда известно значение
letНетВо время выполнения программы
varДаВо время выполнения программы
constНетНа этапе компиляции

Частые ошибки

  • Объявлять всё через var «на всякий случай». Так теряется главный смысл let — гарантия, что значение точно не поменяется. Компилятор Nim не сможет подсказать вам, если где-то в коде вы случайно перепишете то, что должно было остаться постоянным.
  • Пытаться положить в const результат работы программы. Ввод с клавиатуры, чтение файла, текущее время, случайное число — всё это узнаётся только во время выполнения, для таких значений подходит let или var, но не const.
  • Путать let с полной неизменяемостью содержимого. let запрещает переприсвоить САМО имя. Если внутри лежит структура с изменяемыми полями, это отдельная тема — на этом этапе курса просто запомните: let относится к самой переменной, а не заглядывает вглубь значения.

Итоги

  • let — значение вычисляется один раз при выполнении программы и больше не меняется, используйте его по умолчанию.
  • var — обычная изменяемая переменная, нужна там, где значение реально меняется по ходу программы.
  • const — значение известно ещё до запуска программы (компилятору), подходит для настроек и констант.
  • Компилятор Nim проверяет эти правила заранее и не даст собрать программу, если вы попробуете изменить let или const.
Проверьте себя
1. Чем let отличается от var в Nim?
Alet — значение нельзя переприсвоить после объявления, var — можно
Blet работает только с числами, var — с любыми типами
Clet быстрее var при выполнении программы
DРазницы нет, это синонимы
2. Какое значение можно объявить через const?
AРезультат чтения файла во время работы программы
BЧисло секунд в часе, вычисленное из литералов
CТекущее время, полученное функцией now()
DЗначение, введённое пользователем с клавиатуры