Кто они такие?

Утверждения типа времени компиляции — это операторы, которые должны быть логически удовлетворены вашим кодом, прежде чем компилятор сможет успешно его скомпилировать. Мы используем их в основном для обеспечения соответствия типов интерфейсам, таким как:

* http.Pusher, если тип имеет метод Push(string, *http.PushOptions) error
* json .Unmarshaler, если тип имеет метод UnmarshalJSON([]byte) error
* fmt.Stringer, если type имеет метод String() string

* ‹вставьте сюда свой собственный интерфейс›

Мы можем соответствовать интерфейсу с помощью такого утверждения:

var _ ‹интерфейс для удовлетворения› = ‹экземпляр типа, который его удовлетворяет›

Например:

Покажите мне код:

Go обещает, что любой тип, для которого определен пользовательский метод String(),
будет вызывать этот метод каждый раз, когда нам нужно его использовать, скажем, в спецификаторах формата, то есть “% с”

и при запуске печатает
Это одна собака

iff Animal определяет метод String() string, то есть:

поэтому, чтобы убедиться, что эта конформация сохраняется и что наш код правильно удовлетворяет fmt.Stringer, мы будем утверждать при компиляции тип, подобный этому

Конечно, это не обязательно, но если мы случайно забыли
определить String() string, мы были бы очень удивлены, увидев результат:

Это один %!s(main.Animal=1)

Это могло произойти из-за того, что мы написали string() string. Это вполне может случиться и может быть легко упущено даже при проверке кода, несмотря на то, что многие другие смотрят, потому что ошибки с заглавными буквами трудно поймать.

Реальный вариант использования в стандартной библиотеке Go:

Была эта проблема

https://github.com/golang/go/issues/17391, в котором мы обещали, что
math/big.Float будет соответствовать fmt.Scanner но мы на самом деле никогда не применяли это.

Я отправил исправление для него с помощью CL https://go-review.googlesource.com/c/30723
, которое включало реализацию логики для fmt.Scanner. Однако, чтобы гарантировать, что мы никогда не отступим от этого обещания, я добавлю следующее утверждение:

Как это влияет на меня и почему меня это должно волновать?

Мне лично приходится иметь дело с просеиванием очень больших объемов кода и багов, которые сопровождают такие ситуации. Также из-за характера бэкэнд-инжиниринга, которым я занимаюсь, я должен пытаться упростить вещи и легко выявлять такие ошибки.

Обычно при работе с несколькими API мы определяем пользовательские форматы сериализации/десериализации для обработки данных из этих API, чтобы мы могли использовать эти данные по-своему; не так, как это было передано. По сети данные могут быть отправлены в определенной форме для уменьшения избыточности или по определенным причинам, но в коде вы хотели бы извлечь определенные свойства и использовать их по-другому, поэтому нам может потребоваться распутать их.

Мой непосредственный вариант использования:

В настоящее время я пишу небольшой клиент API для сайта USGS Earthquakes, чтобы иметь данные для тестирования моих k-средних и онлайн-реализаций k-средних, а затем визуализировать результаты.

Из данных API GeoJSON по адресу: https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php.

«координаты» определяются как

формы

Проблема, которую нужно решить здесь, заключается в том, что мне нужно преобразовать эти данные JSON в формат, который легко адресуется и доступен, например.

а затем передать эти данные внутреннему API, который легко их использует. Это помогает избежать возни с извлечением столбцов по индексу для каждой внутренней службы, которая использует данные.

Как с этим справиться?

Рад, что вы спросили: Go позволяет нам определить пользовательский десериализатор/демаршалер JSON для каждого типа, если они точно соответствуют методу:
UnmarshalJSON([]byte) error

как указано на https://golang.org/pkg/encoding/json/#Unmarshal

Покажите мне код!

Хождение по натянутой веревке:

Если я допущу опечатку и напишу UnmarshalJSON как Unmarshal, что я делал раньше, или вместо этого наберу unmarshalJSON, вот результаты:

  • Без утверждения типа компиляции:
    я получил бы ошибку во время выполнения после развертывания службы.

Жаль, что мы успешно прошли код-ревью, и хотя семантически правильный, в нашем коде все еще есть ошибка. Вы также можете написать тесты, но для чего-то столь же простого и разрушительного, как опечатка, простой помощник времени компиляции может спасти это излишество.

  • С утверждением типа компиляции
    Компилятор правильно предупреждает меня, что я не удовлетворил интерфейс, с такой ошибкой:

Кому это нужно?

На мой взгляд, все, кто пишет Go. Если, как и я, вы имеете дело с множеством микросервисов и API, то вы знаете, какую боль вызывают ошибки, которые могли быть обнаружены до развертывания, но вместо этого всплыли во время выполнения: отключать некоторые сервисы, чтобы увидеть реакцию, часами отлаживать, тратить время и ресурсы компании, должен ли я продолжать?

Вывод

Компилятор Go — ваш друг, и он здесь, чтобы сделать вас более продуктивным. Давайте воспользуемся его мощью, создадим утверждения типа во время компиляции для выполнения контрактов и обещаний интерфейсам!

Эта практика повысит вашу инженерную производительность, сэкономит ваш доход, сократит время, в течение которого ваши сервисы отключаются, просто заранее сделав утверждения типа компиляции.

PS: вот полный фрагмент примера

На игровой площадке Го: https://play.golang.org/p/HdkOnV90w8

или ниже встроенного. Попробуйте поиграться с ним и посмотрите, как поможет компилятор.