ЗАМЕР
bench/gostruct/example.sh
Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.
- Цитируется в статье
- /ru/interview/golang/structs
- Как запустить
go run bench/gostruct/internals.go bash bench/gostruct/compile.sh go run bench/gostruct/sets.go GOEXPERIMENT=noswissmap go run bench/gostruct/sets.go /usr/local/go1.25.1/bin/go run bench/gostruct/sets.go go run bench/gostruct/practice.go bash bench/gostruct/example.sh
Запись прогона
Замеры для урока «Структуры и методы»
| Файл | Что делает |
|---|---|
internals.go |
восемь наблюдений без единого замера времени: нулевое значение без конструктора, копия при присваивании, передаче и range, поверхностность копии, сравнение == и структура ключом карты, размер и выравнивание при двух порядках полей, пустая структура, метод как функция с получателем и определённый тип без методов базового, анонимная структура; после аудита 14.10.2026 — раздел 9 (slices.Clone/maps.Clone поверхностны, массив указателей копирует указатели, рекурсивный Clone) и раздел 10 (поле any: сравнимо при компиляции, паника при выполнении) |
compile.sh |
собирает cases/* (или проверяет их go vet и golangci-lint) и печатает ответ инструмента: == и ключ карты у структуры со срезом, метод на чужом типе, смешанный и позиционный литерал, копия структуры с sync.Mutex, анализатор fieldalignment |
cases/*/ |
подопытные программы к compile.sh, у каждой свой go.mod; большинство намеренно не собирается |
sets.go |
память карты на миллион строковых ключей со значениями struct{}, bool и int64; размер слота «ключ–значение» |
example.sh, cases/example/ |
полный короткий пример урока (go run . в чистом каталоге с go.mod) |
practice.go |
источник ответов двух практических задач урока |
go run bench/gostruct/internals.go
bash bench/gostruct/compile.sh
go run bench/gostruct/sets.go
GOEXPERIMENT=noswissmap go run bench/gostruct/sets.go
/usr/local/go1.25.1/bin/go run bench/gostruct/sets.go
go run bench/gostruct/practice.go
bash bench/gostruct/example.sh
Записи прогонов — в runs/, go1.24.7 linux/amd64, 2 ядра:
internals.txt, compile.txt, sets.txt, sets-noswissmap.txt
(та же go1.24.7 с прежней реализацией карт), example.txt, practice.txt;
на go1.25.1 — internals-go1.25.txt, compile-go1.25.txt, example-go1.25.txt,
sets-go1.25.txt: отличаются от go1.24.7 только строкой версии. Сверено
14.10.2026; Go 1.26 и 1.27 не измерялись. golangci-lint в compile.sh —
версии 2.5.0, собранный go1.25.1.
Что получилось
Нулевое значение может быть рабочим — если тип так написан. var mu sync.Mutex сразу берётся Lock, var buf bytes.Buffer сразу пишется. Свой
тип Stats, который создаёт карту при первой записи, тоже работает без
конструктора, а Registry, который ждёт карту от конструктора, падает:
assignment to entry in nil map.
Присваивание копирует структуру целиком, но копия поверхностная. После
b := a и правок в b у a не изменились строка и массив [3]int, а
изменились элемент среза, значение в карте и значение под указателем:
unsafe.Sizeof(a) = 80 — копируются заголовки, а не данные за ними. range
по срезу структур отдаёт копию: v.X = 0 не меняет pts[0].X. Копию
структуры с sync.Mutex компилятор пропускает, а go vet ловит в трёх местах.
== у структуры — пока сравнимы все поля. Point{1, 2} == Point{1, 2} —
true, Point работает ключом карты. Структура с полем []string не
сравнивается (struct containing []string cannot be compared) и не может быть
ключом (invalid map key type Team), а за интерфейсом сравнение собирается и
паникует: comparing uncomparable type main.Team; ключом map[any]int —
тоже паника, с другим текстом: hash of unhashable type main.Team. Указатели сравниваются
адресами: две структуры с указателями на равные числа не равны.
Порядок полей меняет размер. struct{ a bool; b int64; c bool } — 24
байта, смещения 0, 8, 16; struct{ b int64; a bool; c bool } — 16, смещения
0, 8, 9. Полезных байт в обоих 10. fieldalignment: struct of size 24 could be 16 — в обычный go vet он не входит, в cases/fieldalign/.golangci.yml
он включён явно.
Пустая структура весит 0 — кроме как в конце другой структуры.
struct{ z struct{}; n int64 } — 8 байт, struct{ n int64; z struct{} } —
16: за пустым полем в конце компилятор добавляет байт
(cmd/compile/internal/types/size.go, строки 517–523 в go1.24.7), и
выравнивание делает из него восемь.
Поэтому map[string]struct{} на go1.24 весит столько же, сколько
map[string]bool. Миллион строковых ключей: 56 МБ, 56 МБ и 56 МБ у
int64, кратность bool к struct{} — 1.00. Слот карты — структура «ключ,
значение», и struct{ key string; elem struct{} } весит те же 24 байта, что
с bool и int64. На go1.25.1 — те же 56 МБ. С прежней реализацией карт
(GOEXPERIMENT=noswissmap) — 40, 42 и 58 МБ: там ключи и значения корзины лежат
раздельно.
Метод — функция с получателем первым аргументом. Celsius.Fahrenheit(100)
даёт то же, что Celsius(100).Fahrenheit(), а тип выражения —
func(main.Celsius) float64. Метод на time.Duration и на int объявить
нельзя: cannot define new methods on non-local type. Новый тип
MyDuration с тем же базовым int64 методов time.Duration не получает:
печатается 1500000000, а не 1.5s.
Литерал: имена полей или позиции. Смешивать нельзя (mixture of field:value and value elements). Позиционный литерал чужого типа собирается,
но go vet предупреждает (struct literal uses unkeyed fields), а после того
как в тип добавили поле, перестаёт собираться: too few values in struct literal of type geo.Point.
Clone тоже поверхностный (раздел 9). После slices.Clone у [][]int
правка c[0][0] видна в исходном (s[0][0] = 99), после maps.Clone у
map[string][]int — тоже; у []int и map[string]int — нет. b := a для
[2]*int копирует указатели: *a[0] = 99. Метод Doc.Clone, клонирующий
каждый внутренний срез, даёт независимую копию: doc.Grid[0][0] = 1.
Поле-интерфейс (раздел 10). type Key struct{ X any } сравним при
компиляции; Key{[]int{1}} == Key{[]int{1}} — паника comparing uncomparable type []int, вставка ключом карты — hash of unhashable type []int.
Скрипт
21 строк#!/usr/bin/env bash
#
# Полный короткий пример урока: файл cases/example/main.go со своим go.mod
# (go 1.24), который можно скопировать в чистый каталог и запустить
# командой go run . — без зависимостей от остальных файлов bench/gostruct.
#
# ЗАЧЕМ. Добавлен после внешнего аудита 14.10.2026: остальные программы
# каталога — стенды наблюдений, их вывод не заменяет запускаемого примера.
#
# ЗАПУСК:
#
# bash bench/gostruct/example.sh
set -u
GO=${GO:-go}
cd "$(dirname "$0")/cases/example"
echo "$($GO env GOVERSION) $($GO env GOOS)/$($GO env GOARCH) | полный пример: go run ."
echo
$GO run .