$ sudo teach IT

Зачем хранить значения

Программа почти всегда что-то запоминает: имя пользователя, счёт в игре, результат вычисления. Чтобы использовать значение больше одного раза, ему дают имя — и обращаются к нему по этому имени, а не переписывают само значение снова и снова. В Swift для этого есть два способа: завести константу (значение, которое нельзя изменить после того, как его задали) и завести переменную (значение, которое можно менять по ходу работы программы).

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

let и var

Официально это называется так: let объявляет константу, var — переменную. Пример:

let daysInWeek = 7
var score = 0

score = 10
print(score)

Здесь daysInWeek — константа: число дней в неделе не меняется, поэтому и переопределять эту константу не нужно. А score — переменная: счёт в игре растёт, поэтому его можно присваивать заново. Присваивание записывается через =: слева имя, справа новое значение.

Почему по умолчанию — let

В коде на Swift принято сначала объявлять значение через let и переключаться на var только тогда, когда без изменения значения правда не обойтись. Причины две.

Во-первых, неизменяемое значение проще держать в голове: если вы видите let, то точно знаете, что это значение останется таким же во всех следующих строках функции. Не нужно пролистывать код в поисках места, где его могли поменять.

Во-вторых, компилятор Swift сам подсказывает, если вы объявили переменную через var, но ни разу её не изменили — он предупредит, что удобнее было бы сделать её константой. Это не ошибка, программа всё равно соберётся, но стоит прислушаться: предупреждение означает, что часть возможностей var в этом месте просто не используется, а лишняя изменяемость — это лишний источник багов в будущем, когда код станет больше.

Аннотация типа и вывод типа

У каждого значения в Swift есть тип — например, Int для целых чисел или String для строк. Тип можно указать явно, это называется аннотация типа:

let x: Int = 5
let name: String = "Марат"

Здесь после имени и двоеточия стоит тип, а после знака равенства — значение. Но чаще всего аннотация не нужна: компилятор сам определяет тип по значению справа от =. Это называется вывод типа (type inference):

let y = 5
let city = "Ташкент"

Компилятор смотрит на 5 и понимает, что y — это Int, а по "Ташкент" определяет, что city — это String. Результат совпадает с явной аннотацией, но кода меньше и он легче читается.

Аннотация типа нужна в двух случаях. Первый — когда у переменной пока нет начального значения:

var age: Int
age = 18
print(age)

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

Второй случай — когда вы хотите тип, который отличается от того, что вывел бы компилятор сам по себе (например, более широкий числовой тип). Но в остальных случаях аннотация — лишний текст, который ничего не добавляет к тому, что и так понятно из значения.

Что будет, если изменить let

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

let limit = 100
limit = 200 // ошибка компиляции: cannot assign to value: 'limit' is a 'let' constant

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

Правила имён

Для имён переменных и констант в Swift принят стиль lowerCamelCase: первое слово с маленькой буквы, каждое следующее — с большой, без пробелов и подчёркиваний: userName, totalScore, isReady. А вот имена типов — Int, String, и те, что вы создадите сами позже, — пишут в стиле UpperCamelCase, с большой буквы: String, Int, UserProfile. Так с первого взгляда на код видно, где значение, а где тип.

Формально Swift разрешает в именах юникод-символы, то есть переменную можно назвать хоть привет, хоть количество — это скомпилируется. Но на практике имена почти всегда пишут по-английски: так код понятен любому разработчику независимо от родного языка, совместим с англоязычной документацией и библиотеками, а автодополнение и поиск по коду работают без сюрпризов с раскладкой клавиатуры.

Что точно нельзя: имя не может начинаться с цифры (1player — ошибка), не может содержать пробел (total score — это уже два слова, а не одно имя) и не может содержать дефис (total-score компилятор прочитает как вычитание total minus score). Разрешены буквы, цифры не в начале и знак подчёркивания.

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

Объявление var для значения, которое ни разу не меняется — работать будет, но компилятор предупредит, а лучше сразу писать let.

Попытка присвоить значение константе во второй раз, в том числе то же самое значение — компилятор не разрешит, даже если число совпадает с тем, что уже было.

Использование переменной или константы без начального значения и без явной аннотации типа — компилятор не может понять тип и откажется собирать код.

Имя с дефисом или пробелом вместо lowerCamelCase — приводит либо к ошибке компиляции, либо к тому, что выражение читается совсем не так, как задумано.

Резюме

  • let — константа, значение задаётся один раз и не меняется; var — переменная, значение можно переприсваивать.
  • По умолчанию используйте let и переключайтесь на var, только когда изменение действительно нужно — компилятор подскажет про лишнюю изменяемость, а неизменяемое значение проще читать.
  • Аннотация типа (let x: Int = 5) нужна для объявления без начального значения или когда нужен тип, который компилятор не вывел бы сам; в остальных случаях достаточно вывода типа.
  • Изменение let — это ошибка компиляции, программа с ней не соберётся, а не упадёт во время работы.
  • Переменные и константы называют в стиле lowerCamelCase, типы — в UpperCamelCase; юникод в именах допустим, но на практике пишут по-английски; имя не может начинаться с цифры и содержать пробел или дефис.

Проверьте себя

4 вопроса

Профиль ученика

Напишите программу, которая объявляет константу name типа String со значением "Алия" и переменную age со значением 16 (тип пусть определит компилятор сам, без явной аннотации). Затем напечатайте по одной строке через print: сначала значение name, затем значение age.

Ожидаемый вывод (ровно две строки):

Алия
16

Пересчёт очков

Напишите программу, которая объявляет переменную score со значением 10. Напечатайте её значение. Затем измените score, увеличив на 5 (то есть присвойте ей результат score + 5), и снова напечатайте её значение.

Ожидаемый вывод (ровно две строки):

10
15