День 12 — глубже
Всё здесь необязательно. Материалы на английском помечены.
Тур по Go: несколько результатов (англ.)
- Multiple results — https://go.dev/tour/basics/6 Та же мысль на трёх строках кода, с кнопкой «Run» прямо на странице.
- Named return values — https://go.dev/tour/basics/7 Именованные результаты и «голый»
returnбез значений. Именно про такойreturnв туре сказано, что в длинных функциях он вредит читаемости; поэтому в курсе мы его не используем.
Спецификация языка (англ.)
- Return statements — https://go.dev/ref/spec#Return_statements Точные правила: сколько значений должно быть в
return, когда их можно не писать, что такое «terminating statement» — из-за него и появляетсяmissing return. - Declarations and scope — https://go.dev/ref/spec#Declarations_and_scope Область видимости словами спецификации: имя видно в блоке, где объявлено, и во всех вложенных. Там же — почему переменная, объявленная в заголовке
ifилиfor, видна и в теле.
Effective Go и блог (англ.)
- Multiple return values — https://go.dev/doc/effective_go#multiple-returns Зачем языку несколько результатов и почему в Go нет «исключений».
- Error handling and Go — https://go.dev/blog/error-handling-and-go Статья 2011 года, из которой выросло всё сегодняшнее соглашение. Первые два раздела читаются легко; дальше идут интерфейсы — это блок 5.
- Errors are values — https://go.dev/blog/errors-are-values Короткая заметка Роба Пайка о том, что ошибка — обычное значение, с которым можно делать обычные вещи.
%w вместо %v — почему не сегодня
В go doc fmt.Errorf упоминается глагол %w:
go doc fmt.Errorf%w делает то же, что %v, но дополнительно сохраняет исходную ошибку внутри новой — так, что потом можно спросить «а не та ли это ошибка была». Спрашивать пока нечем: для этого нужны errors.Is и errors.As, а они идут в блоке 5 вместе с интерфейсами. До тех пор %v даёт ровно то, что нужно сегодня: читаемый текст.
Мини-опыты
1. Именованные результаты и голый return. Наберите в scratch:
scratch/main.gopackage 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.gopackage 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.