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

Короткое объявление :=

var stock int = 40 — четыре слова ради одной ячейки. Внутри функций переменные обычно объявляют короче. На этой странице — короткая запись и разница между := и =.


:= — объявить и сразу присвоить

Ситуация. Внутри функции нужна переменная с начальным значением — самый частый случай.

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

import "fmt"

func main() {
	stock := 40
	item := "Болт М6"
	fmt.Println(item, stock)
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
Болт М6 40

Как это читать. stock := 40 делает то же, что var stock int = 40. Знак := читается «объявить и присвоить». Тип не написан — компилятор выводит его из значения: 40 — int, "Болт М6" — string. Выведенный тип закреплён навсегда: число в item не положить. Так же выводит тип и var, если тип не писать: var price = 1200.

Запись Когда пишут
stock := 40 внутри функции, когда начальное значение известно, — чаще всего
var sold int когда начинать нужно с нулевого значения
var price = 1200 вне функций, где := нельзя

Попробуйте сейчас: объявления через :=.

Цель: убедиться, что := даёт тот же вывод, а выведенный тип закреплён за переменной.

  1. В scratch/main.go с прошлой страницы перепишите через := оба объявления со значением — товар и остаток; объявление продаж без значения оставьте на var. Запустите — вывод не должен измениться:
▶ Выполните
cd ~/gocourse/day02/scratch
go run .
  1. Потом проверьте, что выведенный тип закреплён. Замените содержимое scratch/main.go программой ниже:
✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	stock := 40
	item := 7
	item = "Гайка"
	fmt.Println("Остаток:", stock)
}

и запустите:

▶ Выполните
go run .
  1. Допишите item в Println через запятую после stock и запустите снова.

Готово, когда: первый запуск программы с item дал два сообщения компилятора, второй — одно; разбор — в следующем разделе.


Опыт с item: два сообщения

Что увидите после первого запуска:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:7:2: declared and not used: item
./main.go:8:9: cannot use "Гайка" (untyped string constant) as int value in assignment

Как это читать. Сообщение про тип — второе. Тип item выведен из 7, это int, и строку "Гайка" в эту ячейку не положить.

Первое сообщение — следствие. Строка item = "Гайка" только записывает в ячейку. Использованной переменная считается, когда её значение читают: печатают или ставят в правую часть присваивания. Здесь item ни разу не прочитали, поэтому declared and not used, хотя имя встречается в двух строках.

После второго запуска, с item в Println, сообщение одно:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:8:9: cannot use "Гайка" (untyped string constant) as int value in assignment

Если в выражении или печати заменить переменную на что-то другое, она может перестать читаться — и к основной ошибке добавится declared and not used.


:= только внутри функции

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

import "fmt"

stock := 40

func main() {
	fmt.Println("Остаток:", stock)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:5:1: syntax error: non-declaration statement outside function body

Как это читать. «Не объявление вне тела функции». Вне функций Go принимает только объявления с ключевым словом: package, import, func, var, const. := работает только внутри функции. С var stock = 40 на строке 5 программа соберётся и напечатает Остаток: 40. Но в первых днях курса всё пишем внутри main.


:= и = — разные действия

Знак Что делает Переменной ещё нет Переменная уже есть
:= создаёт новую ячейку и кладёт значение создаёт ошибка
= кладёт значение в существующую ячейку ошибка заменяет значение
Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	stock := 40
	fmt.Println("Утром:", stock)
	stock := 55
	fmt.Println("Днём:", stock)
}
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day02/scratch$ go run .
# day02/scratch
./main.go:8:8: no new variables on left side of :=

«Нет новых переменных слева от :=»: знак обещает создать переменную, а stock уже создана в строке 6. Нужно stock = 55.

Обратная ошибка — stock = 40 без объявления — даёт undefined: stock: положить значение некуда, ячейки нет. Такое сообщение будет на строке stock = 40 и ещё по одному на каждой строке ниже, где упомянута stock.


Сначала объявить, потом использовать

Компилятор читает программу сверху вниз: имя можно использовать только ниже строки, где его объявили.

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

import "fmt"

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

В строке 6 ячейки ещё нет, в строке 7 она появляется, но ниже её никто не читает. Два сообщения — одна причина: строки стоят не в том порядке.


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

Что видите Что это значит Что делать
syntax error: non-declaration statement outside function body := написано вне функции перенести строку в main или писать var
no new variables on left side of := := для переменной, которая уже объявлена заменить := на =
undefined: stock в строке stock = 40, ниже — ещё undefined: stock на каждой строке со stock = для переменной, которой ещё нет первое появление — через := или var
undefined: stock, ниже declared and not used: stock переменную используют выше объявления перенести объявление выше
cannot use "сорок" (untyped string constant) as int value in assignment stock := 40, потом stock = "сорок" класть значение того же типа
declared and not used: item, хотя ниже есть item = … в переменную только записывали, её значение нигде не читается прочитать значение — напечатать или поставить в правую часть — либо удалить переменную

Попробуйте сейчас: почините fix2 и посмотрите правку.

Цель: fix2 собирается, а diff с оригиналом показывает вашу правку — одну строку.

Программа fix2 не собирается.

  1. Запустите и прочитайте сообщение:
▶ Выполните
cd ~/gocourse/day02/fix2
go run .
  1. Исправьте одну строку, сохраните и запустите go run . снова.
  2. Посмотрите свою правку — сравните файл с оригиналом, который курс сохранил при создании стенда:
▶ Выполните
diff ~/.course/orig/day02/fix2/main.go main.go

diff печатает только различающиеся строки. Для примера выше, где в строке 8 := заменили бы на =, вывод такой:

Вывод · это печатает программа, набирать не нужно
8c8
< 	stock := 55
---
> 	stock = 55

8c8 — «строка 8 изменилась»; с < — как было, с > — как стало. Проверка считает строки так же и засчитывает починку, если их одна или две: починить, а не переписать.

  1. Сообщение no new variables on left side of := — в ~/errors.md.

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


Дальше: присваивание — что на самом деле делает = — course next