Deep Engineering

MEASUREMENT

bench/gointerface/methodsets.sh

The script that produced the numbers in the article, and the record of the run. The file is read from the repository at build time — this is the code that was run, not a copy of it.

Cited in
/en/interview/golang/interfaces

The run below is recorded in Russian. It is a lab record, kept in the language it was written in; the numbers, the tables and the code read the same either way.

Record of the run

This measurement has no recorded run — only the script.

Script

79 lines
#!/usr/bin/env bash
#
# Неявная реализация и наборы методов: что говорит КОМПИЛЯТОР.
#
# ЗАЧЕМ СКРИПТ, А НЕ ПРОГРАММА. Обе половины этой темы проверяются сборкой, а
# не запуском. «Тип реализует интерфейс, ничего об этом не объявляя» доказывается
# тем, что программа СОБИРАЕТСЯ; «метод с получателем-указателем не входит в
# набор методов значения» — тем, что не собирается, и текстом ошибки. Ни то, ни
# другое напечатать из работающей программы нельзя.
#
# ПОЧЕМУ ЭТО ВАЖНО ДЛЯ УРОКА. Аудит линии просил начинать интерфейсы с неявной
# реализации и наборов методов, а не с двух машинных слов рантайма. Здесь для
# этого есть доказательство того же сорта, что и для всего остального в линии:
# не «так устроено», а вывод инструмента.
#
# ЗАПУСК:
#
#	bash bench/gointerface/methodsets.sh
set -u

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

cat > "$tmp/go.mod" <<'EOF'
module methodsets

go 1.24
EOF

echo "=== 1. РЕАЛИЗАЦИЯ БЕЗ ОБЪЯВЛЕНИЯ ==="
cat > "$tmp/ok.go" <<'EOF'
package main

import "fmt"

// Интерфейс объявлен здесь и ничего не знает о Point.
type Stringer interface{ String() string }

// Point не упоминает Stringer ни одним словом.
type Point struct{ X, Y int }

func (p Point) String() string { return fmt.Sprintf("(%d,%d)", p.X, p.Y) }

func main() {
	var s Stringer = Point{1, 2}
	fmt.Println("assigned:", s.String())
}
EOF
( cd "$tmp" && go run ok.go )
echo "--- exit code: $?"

echo
echo "=== 2. ПОЛУЧАТЕЛЬ-УКАЗАТЕЛЬ И НАБОР МЕТОДОВ ЗНАЧЕНИЯ ==="
cat > "$tmp/bad.go" <<'EOF'
package main

type Stringer interface{ String() string }

type Point struct{ X, Y int }

func (p *Point) String() string { return "point" }

func main() {
	var byValue Stringer = Point{1, 2}
	var byPointer Stringer = &Point{1, 2}
	_, _ = byValue, byPointer
}
EOF
( cd "$tmp" && go vet ./bad.go 2>&1 | sed 's|^.*/bad.go|bad.go|' )
( cd "$tmp" && go build ./bad.go >/dev/null 2>&1 )
echo "--- go build exit code: $?"

echo
echo "  Первая половина собралась и напечатала результат: Point реализует"
echo "  Stringer, нигде об этом не заявляя. Вторая не собралась, и текст"
echo "  ошибки называет причину сам — получатель у метода указательный,"
echo "  поэтому метод не входит в набор методов ЗНАЧЕНИЯ. Строка с &Point"
echo "  в том же файле законна: у указателя набор шире."