День 02 · Переменные и присваивание · страница 6 из 6

Константы и имена

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


Константа — имя для значения, которое не меняется

Ситуация. Доставка стоит 29900 копеек. Число участвует в расчёте каждого заказа. Напишете его в пяти местах — через месяц никто не вспомнит, что это за число, а при смене тарифа одно место забудут.

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

const deliveryPrice = 29900

func main() {
	itemsPrice := 154000
	total := itemsPrice + deliveryPrice
	fmt.Println("Товары, коп.:", itemsPrice)
	fmt.Println("Доставка, коп.:", deliveryPrice)
	fmt.Println("Итого, коп.:", total)
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Товары, коп.: 154000
Доставка, коп.: 29900
Итого, коп.: 183900

Как это читать. const deliveryPrice = 29900 — константа: имя для значения, которое известно до запуска и не меняется, пока программа работает. Тип выводится из значения. Имя объясняет смысл числа, а само число задано в одном месте. Константы, нужные всей программе, объявляют над func main.

Переменная Константа
объявление var или := const
поменять значение можно нельзя — ошибка компиляции
какое значение любое, в том числе посчитанное во время работы только известное до запуска
объявили и не используете ошибка declared and not used не ошибка

Ещё примеры из нашей области:

Код для чтения · разбираем, набирать не нужно
const vatPercent = 22
const maxItemsPerOrder = 50
const warehouseName = "Северный"

Константу нельзя изменить

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

const deliveryPrice = 29900

func main() {
	deliveryPrice = 0
	fmt.Println("Доставка, коп.:", deliveryPrice)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:8:2: cannot assign to deliveryPrice (neither addressable nor a map index expression)

Как это читать. Сообщение то же, что у 55 = stock на третьей странице, и читается так же: важна первая половина, cannot assign to — слева стоит то, чему присваивать нельзя. Константа — не ячейка: компилятор подставляет её значение прямо туда, где она упомянута, как если бы там было написано 29900. Записать некуда. deliveryPrice++ даёт то же сообщение.

Нужно значение, которое начинается с константы, а дальше меняется, — скопируйте константу в переменную: price := deliveryPrice. Имя у новой переменной своё, а не имя константы: deliveryPrice := deliveryPrice - 100 собирается, но константу не освобождает — рядом встаёт вторая переменная с тем же именем, и внутри функции видна только она. Другие ошибки с константами — в таблице в конце страницы.


Попробуйте сейчас: почините fix3, не трогая константу.

Цель: fix3 собирается и печатает цену и цену со скидкой, а строка с const осталась как была.

В fix3 цена товара — константа, а программа пытается её изменить.

  1. Запустите и прочитайте сообщение:
▶ Выполните
cd ~/gocourse/day02/fix3
go run .
  1. Строку с const не трогайте: меняется не цена товара, а то, что платит покупатель. Для цены со скидкой заведите свою переменную, и имя ей возьмите другое — final или toPay. Два коротких пути проверка не засчитает: const → var (цена перестаёт быть константой) и price := price - discount (вторая переменная с тем же именем). Изменить можно не больше двух строк.
  2. Сообщение — в ~/errors.md.

Готово, когда: go run . печатает две строки из комментария в начале файла — пункт проверки fix3.


Правила имён

Имя переменной или константы:

Больше всего сообщений даёт дефис. Переменную cellA набрали как cell-a:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	cell-a := 5
	fmt.Println("В ячейке:", cell-a)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:6:2: undefined: cell
./main.go:6:2: non-name cell - a on left side of :=
./main.go:6:7: undefined: a
./main.go:7:34: undefined: cell
./main.go:7:39: undefined: a

Как это читать. Дефис — это минус. Компилятор читает строку 6 как cell - a := 5: слева от := стоит не имя, а выражение «cell минус a», отсюда non-name cell - a on left side of :=. Переменных cell и a нет, отсюда undefined — по сообщению на каждое упоминание в строках 6 и 7. Первым напечатано undefined: cell, а причина названа во втором сообщении: сообщения про одну строку читайте вместе. Остальные поломки имён — в таблице в конце страницы.

Соглашения — компилятор их не требует, но так пишут все:


Почему латиница

Go разрешает буквы любого алфавита:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	остаток := 40
	fmt.Println("Остаток:", остаток)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Остаток: 40

И всё же в курсе имена пишутся латиницей. Главная причина — буквы-двойники: кириллические «с», «о», «р», «а», «е», «х» на экране не отличить от латинских, но для компилятора это разные символы. Здесь в Println одна буква набрана в русской раскладке:

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := 40
	fmt.Println("Остаток:", stoсk)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:6:2: declared and not used: stock
./main.go:7:33: undefined: stoсk

Имена выглядят одинаково, а компилятор видит две разные переменные. Глазами такую ошибку не найти. Когда весь код латиницей — ключевые слова, пакеты, имена, — двойникам неоткуда взяться. К тому же чужой код, документация и коллеги из других стран — всё на латинице. Строки в кавычках и комментарии пишите по-русски: там кириллица на своём месте.


Попробуйте сейчас: сломайте имя тремя способами.

Цель: увидеть сообщения компилятора о неправильных именах и найти каждое в таблице ниже.

В scratch/main.go сломайте имя переменной тремя способами:

  1. начните его с цифры;
  2. вставьте дефис;
  3. наберите одну букву в русской раскладке.

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

▶ Выполните
cd ~/gocourse/day02/scratch
go run .

Готово, когда: для всех трёх поломок сообщения найдены в таблице, а программа снова собирается.


Что может пойти не так

Сообщения про имена в таблице — для main из двух строк: объявление переменной и Println с ней. Поэтому сообщений по два и больше.

Что видите Что это значит Что делать
cannot assign to deliveryPrice (neither addressable nor a map index expression) попытка изменить константу через =, += или ++ завести переменную с начальным значением из константы
missing init expr for vatPercent const vatPercent без значения у константы значение обязательно
itemsPrice + deliveryPrice (value of type int) is not constant const total = itemsPrice + deliveryPrice: в выражении переменная результат — в переменную: total := …
syntax error: unexpected name cells at end of statement, ниже syntax error: unexpected name cells in argument list; possibly missing comma or ) имя с цифры, 2cells := 5: прочитано число 2 и лишнее слово начать имя с буквы: cells2
undefined: cell, ниже non-name cell - a on left side of := и ещё три строки undefined дефис в имени cell-a — это минус cellA
syntax error: unexpected name price at end of statement, ниже syntax error: unexpected name price in argument list; possibly missing comma or ) пробел делит имя delivery price на два слова deliveryPrice
syntax error: unexpected :=, expected name, ниже syntax error: unexpected keyword type, expected expression имя type — ключевое слово другое имя: itemType
declared and not used: stock, ниже undefined: Stock объявлена stock, а в Println — Stock: регистр важен написать имя буква в букву
declared and not used: stock, ниже undefined: stoсk, хотя имена на вид одинаковые в одном из имён буква набрана в русской раскладке стереть имя и набрать заново в латинской раскладке

Итог дня

Запись Что делает
var stock int = 40 объявить переменную с типом и значением
var sold int объявить с нулевым значением: 0 у int, "" у string
stock := 40 объявить внутри функции, тип из значения
stock = 55 заменить значение существующей переменной
stock += 15, stock++ сначала правая часть по текущему значению, потом запись
oldPrice := price скопировать значение; дальше переменные независимы
cellA, cellB = cellB, cellA обмен: все правые части считаются до записи
const deliveryPrice = 29900 значение, которое не меняется

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


Словарик ошибок

Формат строки тот же, что вчера: текст ошибки, тире с пробелами вокруг, что это значило. Строку без тире проверка не засчитает. Двоеточие не подходит: оно есть в каждом сообщении компилятора.


Попробуйте сейчас: словарик и проверка дня.

Цель: в ~/errors.md не меньше пяти разборов, и course check 02 показал итог дня.

  1. Откройте словарик:
▶ Выполните
code ~/errors.md
  1. К вчерашним разборам добавьте сегодняшние, всего минимум пять. Образец строки — один, дальше вторую половину пишете сами:
Образец · набирать не нужно
declared and not used: left — посчитал значение в переменную и забыл её использовать
no new variables on left side of := —
cannot assign to price (neither addressable nor a map index expression) —

Разбор пишите своими словами, про свой случай: списанная строка в словарике ничего не стоит — он нужен вам, а не проверке.

  1. Потом — итог и проверка:
▶ Выполните
course lab 02
course check 02

Готово, когда: course check 02 засчитал пункт errors, а по остальным пунктам вы сверились с course lab 02.


Материал дня закончен. Задания — course lab 02, дополнительное чтение — course extra 02