День 12 · дополнительные материалы

День 12 — глубже

Всё здесь необязательно. Материалы на английском помечены.


Тур по Go: несколько результатов (англ.)

Спецификация языка (англ.)

Effective Go и блог (англ.)

%w вместо %v — почему не сегодня

В go doc fmt.Errorf упоминается глагол %w:

▶ Выполните
go doc fmt.Errorf

%w делает то же, что %v, но дополнительно сохраняет исходную ошибку внутри новой — так, что потом можно спросить «а не та ли это ошибка была». Спрашивать пока нечем: для этого нужны errors.Is и errors.As, а они идут в блоке 5 вместе с интерфейсами. До тех пор %v даёт ровно то, что нужно сегодня: читаемый текст.

Мини-опыты

1. Именованные результаты и голый return. Наберите в scratch:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func pack(qty int, size int) (boxes int, rest int) {
	boxes = qty / size
	rest = qty % size
	return
}

func main() {
	fmt.Println(pack(17, 5))
}

return без значений возвращает то, что лежит в именованных результатах. Уберите строку rest = qty % size — программа соберётся и напечатает второй результат нулём. Ошибки не будет: именованные результаты созданы заголовком и начинаются с нулевых значений. Отсюда и правило курса — писать значения в return явно.

2. Две ошибки с одним текстом. Проверьте обещание из go doc errors.New:

✎ Наберите в файл scratch/main.go
package main

import (
	"errors"
	"fmt"
)

func main() {
	a := errors.New("пустое количество")
	b := errors.New("пустое количество")
	fmt.Println(a == b)
	fmt.Println(a.Error() == b.Error())
}

Первая строка — false, вторая — true. Значения разные, тексты одинаковые. a.Error() — способ получить текст ошибки строкой; в блоке 5 станет понятно, почему это записывается так.

3. Затенение под отладчиком. Возьмите программу со страницы 5, где stock := stock - 40 внутри if, и пройдите её dlv из дня 09:

▶ Выполните
cd ~/gocourse/day12/scratch
dlv debug

Поставьте точку останова на строке с fmt.Println внутри блока — в той программе это десятая строка (break main.go:10), — запустите continue, потом locals. В списке будут две переменные stock с разными значениями: внешняя показана в скобках. Это самый наглядный способ увидеть, что имён одинаковых, а ячеек две.

4. Свой анализатор на затенение. go vet о затенении молчит, но у команды Go есть отдельный анализатор shadow — он живёт в golang.org/x/tools и запускается как go vet -vettool=…. Ставить его в курсе мы не будем: в машине нет сети, и лишние инструменты только отвлекают. Знать полезно другое — что молчание go vet не означает «дефекта нет».


Что почитать про имена

Короткое правило, которое экономит много времени при отладке: не давайте разным вещам одинаковые имена в соседних блоках. Если внутри if понадобилось «то же самое, но пересчитанное», назовите его иначе — stockAfter, keepLeft. Компилятор не запретит одинаковые имена, зато человек, читающий код через месяц, не спутает две ячейки. Это же правило в приложении к коротким именам разобрано в Go Code Review Comments — https://go.dev/wiki/CodeReviewComments (англ.), раздел Variable Names.