Типовые дефекты
Дефекты, которые компилируются, повторяются. Их немного видов, и у каждого свой узнаваемый симптом. Список сокращает третий шаг методики: увидели симптом — сначала проверяете типовые причины, а не всё подряд.
Ни один дефект со страницы компилятор не замечает. go vet на всех семи фрагментах этой страницы тоже молчит — это проверено.
В фрагментах 1–6 показаны только строки из тела main — вставляйте их между { и } в scratch/main.go. С одним import "fmt" первая строка тела — шестая. Седьмому фрагменту нужны другие пакеты, поэтому он показан программой целиком.
1. Граница цикла
Симптом: не хватает первого или последнего элемента, или один лишний.
first := 7
last := 10
for shelf := first; shelf < last; shelf++ {
fmt.Println("Проверена полка", shelf)
}stagiaire@lab:~/gocourse/day09/scratch$ go run .
Проверена полка 7
Проверена полка 8
Проверена полка 9Полка 10 не проверена: < не пропускает последнее значение. Как ловить: три вопроса к заголовку из дня 08 — первое значение, последнее, сколько проходов — и запуск на одном элементе.
2. Порядок условий
Симптом: крупные значения попадают в ветку для средних.
sum := 250000
if sum >= 100000 {
fmt.Println("Скидка 5%")
} else if sum >= 200000 {
fmt.Println("Скидка 10%")
}stagiaire@lab:~/gocourse/day09/scratch$ go run .
Скидка 5%Первая истинная ветка забирает всё, до второй очередь не доходит никогда. Как ловить: подставить значение больше всех порогов. Пороги с >= идут от большего к меньшему — день 06.
3. Деление до умножения
Симптом: копейки теряются, итог чуть меньше верного.
price := 1999
percent := 15
fmt.Println(price/100*percent, price*percent/100)stagiaire@lab:~/gocourse/day09/scratch$ go run .
285 2991999/100 — целое деление, сразу 19: 99 копеек отброшены и уже не умножаются. Как ловить: цена, не кратная 100. Сначала умножать, делить — последним.
4. Накопитель внутри цикла
Симптом: «итог» всегда равен последнему значению.
for day := 1; day <= 3; day++ {
shipped := 0
shipped += 40
fmt.Println("День", day, "отгружено с начала недели:", shipped)
}stagiaire@lab:~/gocourse/day09/scratch$ go run .
День 1 отгружено с начала недели: 40
День 2 отгружено с начала недели: 40
День 3 отгружено с начала недели: 40Как ловить: найти строку, где накопитель объявлен. Внутри цикла он обнуляется на каждом проходе; объявлять его нужно до цикла — день 08.
5. Перепутанные переменные
Симптом: правильные числа стоят не на своих местах.
n := 3
qty := 25
fmt.Printf("Заказ %d: %d шт.\n", qty, n)stagiaire@lab:~/gocourse/day09/scratch$ go run .
Заказ 25: 3 шт.Оба значения — целые, поэтому ни компилятор, ни go vet не возражают. Чаще всего путают похожие переменные: номер строки и счётчик, остаток и заказ. Как ловить: locals в отладчике и сверка каждой переменной с ожиданием.
6. Лишний или недостающий break
Симптом: цикл кончается раньше, чем нужно, — или находит не первое подходящее, а последнее.
for cell := 1; cell <= 5; cell++ {
if cell == 3 {
fmt.Println("Ячейка 3 на ремонте")
break
}
fmt.Println("Проверена ячейка", cell)
}stagiaire@lab:~/gocourse/day09/scratch$ go run .
Проверена ячейка 1
Проверена ячейка 2
Ячейка 3 на ремонтеЯчейки 4 и 5 не проверены: пропустить одну ячейку — это continue, а break заканчивает весь цикл. Обратный случай — break забыли: поиск «первой подходящей» не останавливается и доходит до последней. Как ловить: точка останова внутри if — сколько раз программа там остановится.
7. Строка без TrimSpace
Симптом: одинаковые на вид строки не равны.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if scanner.Text() == "отмена" {
fmt.Println("Заказ отменён")
} else {
fmt.Printf("Непонятная команда: %q\n", scanner.Text())
}
}
}stagiaire@lab:~/gocourse/day09/scratch$ printf 'отмена\nотмена \n' | go run .
Заказ отменён
Непонятная команда: "отмена "Без %q пробел в конце не был бы виден. Как ловить: %q в отладочной печати или print в отладчике; сравнивать strings.TrimSpace(scanner.Text()).
Сводка
| Симптом | Что проверить первым |
|---|---|
| не хватает крайнего элемента или есть лишний | границу цикла: < или <=, с чего начали |
| большие значения обработаны как средние | порядок условий |
| итог меньше на копейки | деление до умножения |
| итог равен последнему значению | где объявлен накопитель |
| числа верные, но не там | перепутанные переменные |
| цикл закончился рано или нашёл последнее вместо первого | break и continue |
| одинаковые на вид строки не равны | пробелы: %q, TrimSpace |
Попробуйте сейчас: bug3 и строка с пробелом.
Цель:
bug3выбирает первую подходящую ячейку, а предсказание для программы со строкой с пробелом сдано.1. Задание
bug3:▶ Выполнитеcd ~/gocourse/day09/bug3 cat TASK.txt go run . < input.txt2. Какой симптом из сводки подходит? Назовите гипотезу, проверьте её печатью или точкой останова, почините минимально и посмотрите правку. Проверьте форматирование —
gofmt -l .ничего не печатает:▶ Выполнитеdiff ~/.course/orig/day09/bug3/main.go main.go gofmt -l .3. Потом вставьте в
~/gocourse/day09/scratch/main.goпрограмму целиком и, не запуская, предскажите, что она напечатает:✎ Наберите в файлscratch/main.gopackage main import ( "fmt" "strings" ) func main() { status := "отгружен " if status == "отгружен" { fmt.Println("да") } else { fmt.Println("нет") } fmt.Println(len(status), len(strings.TrimSpace(status))) }4. Сдайте ответ:
▶ Выполните · выделенное замените своимcourse answer day09.q6 ОТВЕТВместо
ОТВЕТ— всё, что напечатает программа, одной строкой через пробел.lenсчитает байты, кириллическая буква занимает два — день 04.5. Потом запустите и сверьте с тем, что сдали:
▶ Выполнитеcd ~/gocourse/day09/scratch go run .Готово, когда: вывод
bug3наinput.txtсовпадает с «ожидалось» изTASK.txt, вdiffс оригиналом одна-две строки,gofmt -l .ничего не печатает, предсказание для программы со строкой с пробелом сверено запуском, а пунктыbug3,q6иbugfmtвcourse check 09зелёные.
Словарик ошибок
Сообщения отладчика — такие же записи для ~/errors.md, как сообщения компилятора. Сегодня добавьте хотя бы две: например, Stdin is not a terminal, could not find statement, could not find symbol value for, has exited with status 0, expected '==', found '='. Формат прежний: текст сообщения, через тире — что оно значило у вас. Проверка считает словарик накопительно, с дня 01: к концу дня 09 в нём должно набраться минимум восемнадцать записей всего, а не восемнадцать новых. Если их уже больше — пункт зелёный, дописывать до числа не нужно.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
gofmt -l . печатает main.go |
добавленная строка без отступа или с пробелами вместо табуляции | gofmt -w . или Ctrl+S в VS Code |
course check: изменено строк больше лимита |
переписали больше, чем требует причина | diff с оригиналом; начать заново: cp ~/.course/orig/day09/bug3/main.go ~/gocourse/day09/bug3/ |
Пример из TASK.txt сходится, а скрытый случай — нет |
починен симптом под свой input.txt, причина осталась |
вернитесь к гипотезе: какая причина, а не какое число |
course check: строка 1, программа напечатала: «отладка: …» |
в задании осталась отладочная печать | удалите её |
./main.go:36:2: break is not in a loop, switch, or select |
break стоит после закрывающей скобки цикла |
break — внутрь if, в цикле |
Материал дня закончен. Задания — course lab 09, дополнительное чтение — course extra 09