Deep Engineering

ЗАМЕР

bench/gointerface/methodsets.sh

Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.

Цитируется в статье
/ru/interview/golang/interfaces

Запись прогона

У этого замера записи прогона нет — только скрипт.

Скрипт

79 строк
#!/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 "  в том же файле законна: у указателя набор шире."