diff options
| author | 2026-02-02 18:57:20 +0300 | |
|---|---|---|
| committer | 2026-02-02 18:57:20 +0300 | |
| commit | 6c4cbf8578d8a94964ca7327a7826c7c094f94fc (patch) | |
| tree | 07c5dc52358b5725805a465310bbdfa1f4da82bb /content/pages/gostyleguide/google/best-practices.md | |
| parent | Большая чистка блога (diff) | |
| download | blog-6c4cbf8578d8a94964ca7327a7826c7c094f94fc.tar.gz blog-6c4cbf8578d8a94964ca7327a7826c7c094f94fc.tar.bz2 blog-6c4cbf8578d8a94964ca7327a7826c7c094f94fc.tar.xz blog-6c4cbf8578d8a94964ca7327a7826c7c094f94fc.zip | |
Fix images
Diffstat (limited to 'content/pages/gostyleguide/google/best-practices.md')
| -rw-r--r-- | content/pages/gostyleguide/google/best-practices.md | 863 |
1 files changed, 409 insertions, 454 deletions
diff --git a/content/pages/gostyleguide/google/best-practices.md b/content/pages/gostyleguide/google/best-practices.md index 4fc59b1..f7a42c7 100644 --- a/content/pages/gostyleguide/google/best-practices.md +++ b/content/pages/gostyleguide/google/best-practices.md @@ -1,15 +1,12 @@ --- -order: 1 -title: Google Go Style Guide — Лучшие практики +weight: 30 +title: Лучшие практики --- # Лучшие практики стиля Go (Go Style Best Practices) Оригинал: https://google.github.io/styleguide/go/best-practices -[Обзор](https://neonxp.ru/pages/gostyleguide/google/) | [Руководство](https://neonxp.ru/pages/gostyleguide/google/guide) | [Решения](https://neonxp.ru/pages/gostyleguide/google/decisions) | -[Лучшие практики](https://neonxp.ru/pages/gostyleguide/google/best-practices) - **Примечание:** Это часть серии документов, описывающих [Стиль Go (Go Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный документ **не является ни [нормативным (normative)](https://neonxp.ru/pages/gostyleguide/google/#normative), ни [каноническим (canonical)](https://neonxp.ru/pages/gostyleguide/google/#canonical)**, @@ -44,14 +41,13 @@ Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный д будет прочитано. Рассмотрите следующие рекомендации, чтобы избежать избыточного [повторения (repetition)](https://neonxp.ru/pages/gostyleguide/google/decisions/#repetition) в месте вызова (call site): -* Следующее, как правило, можно опустить в именах функций и методов: - - * Типы входных и выходных данных (если нет конфликта) - * Тип получателя (receiver) метода - * Является ли входной или выходной параметр указателем (pointer) +- Следующее, как правило, можно опустить в именах функций и методов: + - Типы входных и выходных данных (если нет конфликта) + - Тип получателя (receiver) метода + - Является ли входной или выходной параметр указателем (pointer) -* Для функций не следует [повторять имя - пакета](https://neonxp.ru/pages/gostyleguide/google/decisions/#repetitive-with-package). +- Для функций не следует [повторять имя + пакета](https://neonxp.ru/pages/gostyleguide/google/decisions/#repetitive-with-package). ```go // Плохо: @@ -67,7 +63,7 @@ Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный д func Parse(input string) (*Config, error) ``` -* Для методов не следует повторять имя получателя метода. +- Для методов не следует повторять имя получателя метода. ```go // Плохо: @@ -79,7 +75,7 @@ Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный д func (c *Config) WriteTo(w io.Writer) (int64, error) ``` -* Не повторяйте имена переменных, передаваемых в качестве параметров. +- Не повторяйте имена переменных, передаваемых в качестве параметров. ```go // Плохо: @@ -91,7 +87,7 @@ Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный д func Override(dest, source *Config) error ``` -* Не повторяйте имена и типы возвращаемых значений. +- Не повторяйте имена и типы возвращаемых значений. ```go // Плохо: @@ -118,8 +114,8 @@ func (c *Config) WriteBinaryTo(w io.Writer) (int64, error) Существуют и другие общие соглашения при выборе имен для функций и методов: -* Функции, которые что-то возвращают, получают имена, похожие на - существительные. +- Функции, которые что-то возвращают, получают имена, похожие на + существительные. ```go // Хорошо: @@ -134,15 +130,15 @@ func (c *Config) WriteBinaryTo(w io.Writer) (int64, error) func (c *Config) GetJobName(key string) (value string, ok bool) ``` -* Функции, которые что-то делают, получают имена, похожие на глаголы. +- Функции, которые что-то делают, получают имена, похожие на глаголы. ```go // Хорошо: func (c *Config) WriteDetail(w io.Writer) (int64, error) ``` -* Идентичные функции, которые отличаются только типами, включают имя типа в - конце имени. +- Идентичные функции, которые отличаются только типами, включают имя типа в + конце имени. ```go // Хорошо: @@ -174,8 +170,7 @@ func (c *Config) WriteBinaryTo(w io.Writer) (int64, error) образом, если ваш код использует фейки или другой вид тестового дубля. [именование]: guide#naming -[тестовые дубли (test doubles)]: - https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts +[тестовые дубли (test doubles)]: https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts Предположим, у вас есть хорошо сфокусированный пакет, предоставляющий production-код, подобный этому: @@ -212,7 +207,7 @@ func (s *Service) Charge(c *Card, amount money.Money) error { /* опущено #### Создание вспомогательных тестовых пакетов (Creating test helper packages) Предположим, вы хотите создать пакет, содержащий тестовые дубли для другого -пакета. Воспользуемся `package creditcard` (из примера выше): +пакета. Воспользуемся `package creditcard` (из примера выше): Один из подходов — создать новый Go-пакет на основе production-пакета для тестирования. Безопасный выбор — добавить слово `test` к оригинальному имени @@ -274,8 +269,8 @@ go_library( См. также: -* [Go Tip #42: Authoring a Stub for - Testing](https://google.github.io/styleguide/go/index.html#gotip) +- [Go Tip #42: Authoring a Stub for + Testing](/pages/gostyleguide/google/index.html#gotip) <a id="naming-doubles-multiple-behaviors"></a> @@ -436,8 +431,8 @@ func TestProcessor(t *testing.T) { ### Затенение (Shadowing) -**Примечание:** Это объяснение использует два неформальных термина, *stomping* и -*shadowing*. Они не являются официальными концепциями в спецификации языка Go. +**Примечание:** Это объяснение использует два неформальных термина, _stomping_ и +_shadowing_. Они не являются официальными концепциями в спецификации языка Go. Как и во многих языках программирования, в Go есть изменяемые переменные: присваивание переменной меняет ее значение. @@ -454,7 +449,7 @@ func abs(i int) int { При использовании [короткого объявления переменных (short variable declarations)] с оператором `:=` в некоторых случаях новая переменная не -создается. Мы можем назвать это *stomping* (затирание). Это допустимо, когда +создается. Мы можем назвать это _stomping_ (затирание). Это допустимо, когда исходное значение больше не нужно. ```go @@ -477,7 +472,7 @@ func (s *Server) innerHandler(ctx context.Context, req *pb.MyRequest) *pb.MyResp ``` Однако будьте осторожны с использованием короткого объявления переменных в новой -области видимости: это вводит новую переменную. Мы можем назвать это *shadowing* +области видимости: это вводит новую переменную. Мы можем назвать это _shadowing_ (затенение) исходной переменной. Код после конца блока ссылается на оригинал. Вот ошибочная попытка условно сократить срок действия (deadline): @@ -536,8 +531,7 @@ func LongFunction() { } ``` -[короткого объявления переменных (short variable declarations)]: - https://go.dev/ref/spec#Short_variable_declarations +[короткого объявления переменных (short variable declarations)]: https://go.dev/ref/spec#Short_variable_declarations <a id="util-packages"></a> @@ -549,7 +543,7 @@ func LongFunction() { Имена пакетов Go должны быть [связаны с тем, что предоставляет пакет](https://neonxp.ru/pages/gostyleguide/google/decisions/#package-names). Называть пакет просто `util`, `helper`, `common` или подобным обычно плохой выбор (хотя это может быть использовано как -*часть* имени). Неинформативные имена затрудняют чтение кода, и если они +_часть_ имени). Неинформативные имена затрудняют чтение кода, и если они используются слишком широко, они могут вызывать ненужные [конфликты импорта](https://neonxp.ru/pages/gostyleguide/google/decisions/#import-renaming). @@ -592,12 +586,12 @@ b := helper.Marshal(curve, x, y) Пользователи видят [godoc] для пакета на одной странице, и любые экспортированные методы типов, предоставляемых пакетом, группируются по их типу. Godoc также группирует конструкторы вместе с типами, которые они возвращают. -Если *клиентскому коду* (client code) вероятно потребуется, чтобы два значения +Если _клиентскому коду_ (client code) вероятно потребуется, чтобы два значения разных типов взаимодействовали друг с другом, может быть удобно для пользователя иметь их в одном пакете. Код внутри пакета имеет доступ к неэкспортированным идентификаторам пакета. Если -у вас есть несколько связанных типов, *реализация* которых тесно связана, +у вас есть несколько связанных типов, _реализация_ которых тесно связана, размещение их в одном пакете позволяет достичь этой связи без загрязнения публичного API этими деталями. Хороший тест для этой связи — представить гипотетического пользователя двух пакетов, где пакеты охватывают тесно связанные @@ -611,7 +605,7 @@ Godoc также группирует конструкторы вместе с предоставление ему собственного небольшого пакета может облегчить его использование. Короткое имя пакета, известное клиентам, вместе с именем экспортированного типа работают вместе, чтобы создать значимый идентификатор: -например, `bytes.Buffer`, `ring.New`. [Пост об именах пакетов][blog-pkg-names] +например, `bytes.Buffer`, `ring.New`. [Пост об именах пакетов][blog-pkg-names] содержит больше примеров. Стиль Go гибок относительно размера файлов, потому что сопровождающие могут @@ -637,64 +631,54 @@ Godoc также группирует конструкторы вместе с Несколько неканонических справочных примеров, чтобы помочь продемонстрировать эти идеи на практике: -* маленькие пакеты, содержащие одну связную идею, которая не требует - добавления или удаления чего-либо еще: - - * [пакет `csv`][package `csv`]: кодирование и декодирование данных CSV с - разделением ответственности соответственно между [reader.go] и - [writer.go]. - * [пакет `expvar`][package `expvar`]: "белый ящик" (whitebox) телеметрии - программы, полностью содержащийся в [expvar.go]. - -* пакеты умеренного размера, содержащие одну большую предметную область и - несколько связанных с ней ответственностей: - - * [пакет `flag`][package `flag`]: управление флагами командной строки, - полностью содержащееся в [flag.go]. - -* большие пакеты, которые разделяют несколько тесно связанных предметных - областей по нескольким файлам: - - * [пакет `http`][package `http`]: ядро HTTP: [client.go][http-client], - поддержка HTTP-клиентов; [server.go][http-server], поддержка - HTTP-серверов; [cookie.go], управление куками. - * [пакет `os`][package `os`]: кроссплатформенные абстракции операционной - системы: [exec.go], управление подпроцессами; [file.go], управление - файлами; [tempfile.go], временные файлы. +- маленькие пакеты, содержащие одну связную идею, которая не требует + добавления или удаления чего-либо еще: + - [пакет `csv`][package `csv`]: кодирование и декодирование данных CSV с + разделением ответственности соответственно между [reader.go] и + [writer.go]. + - [пакет `expvar`][package `expvar`]: "белый ящик" (whitebox) телеметрии + программы, полностью содержащийся в [expvar.go]. + +- пакеты умеренного размера, содержащие одну большую предметную область и + несколько связанных с ней ответственностей: + - [пакет `flag`][package `flag`]: управление флагами командной строки, + полностью содержащееся в [flag.go]. + +- большие пакеты, которые разделяют несколько тесно связанных предметных + областей по нескольким файлам: + - [пакет `http`][package `http`]: ядро HTTP: [client.go][http-client], + поддержка HTTP-клиентов; [server.go][http-server], поддержка + HTTP-серверов; [cookie.go], управление куками. + - [пакет `os`][package `os`]: кроссплатформенные абстракции операционной + системы: [exec.go], управление подпроцессами; [file.go], управление + файлами; [tempfile.go], временные файлы. См. также: -* [Пакеты тестовых дублей (Test double packages)](#naming-doubles) -* [Organizing Go Code (Blog Post)] -* [Organizing Go Code (Presentation)] +- [Пакеты тестовых дублей (Test double packages)](#naming-doubles) +- [Organizing Go Code (Blog Post)] +- [Organizing Go Code (Presentation)] [blog-pkg-names]: https://go.dev/blog/package-names [пакет `bytes`]: https://go.dev/src/bytes/ [Organizing Go Code (Blog Post)]: https://go.dev/blog/organizing-go-code [Organizing Go Code (Presentation)]: https://go.dev/talks/2014/organizeio.slide [пакет `csv`]: https://pkg.go.dev/encoding/csv -[reader.go]: - https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/reader.go -[writer.go]: - https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/writer.go +[reader.go]: https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/reader.go +[writer.go]: https://go.googlesource.com/go/+/refs/heads/master/src/encoding/csv/writer.go [пакет `expvar`]: https://pkg.go.dev/expvar -[expvar.go]: - https://go.googlesource.com/go/+/refs/heads/master/src/expvar/expvar.go +[expvar.go]: https://go.googlesource.com/go/+/refs/heads/master/src/expvar/expvar.go [пакет `flag`]: https://pkg.go.dev/flag [flag.go]: https://go.googlesource.com/go/+/refs/heads/master/src/flag/flag.go [godoc]: https://pkg.go.dev/ [пакет `http`]: https://pkg.go.dev/net/http -[http-client]: - https://go.googlesource.com/go/+/refs/heads/master/src/net/http/client.go -[http-server]: - https://go.googlesource.com/go/+/refs/heads/master/src/net/http/server.go -[cookie.go]: - https://go.googlesource.com/go/+/refs/heads/master/src/net/http/cookie.go +[http-client]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/client.go +[http-server]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/server.go +[cookie.go]: https://go.googlesource.com/go/+/refs/heads/master/src/net/http/cookie.go [пакет `os`]: https://pkg.go.dev/os [exec.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/exec.go [file.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/file.go -[tempfile.go]: - https://go.googlesource.com/go/+/refs/heads/master/src/os/tempfile.go +[tempfile.go]: https://go.googlesource.com/go/+/refs/heads/master/src/os/tempfile.go <a id="imports"></a> @@ -708,8 +692,8 @@ Godoc также группирует конструкторы вместе с их межъязыковой природы. Соглашение для переименованных импортов proto основано на правиле, которое сгенерировало пакет: -* Суффикс `pb` обычно используется для правил `go_proto_library`. -* Суффикс `grpc` обычно используется для правил `go_grpc_library`. +- Суффикс `pb` обычно используется для правил `go_proto_library`. +- Суффикс `grpc` обычно используется для правил `go_grpc_library`. Часто используется одно слово, описывающее пакет: @@ -722,9 +706,9 @@ import ( ``` Следуйте рекомендациям по стилю для [имен -пакетов](https://google.github.io/styleguide/go/decisions#package-names). +пакетов](/pages/gostyleguide/google/decisions#package-names). Предпочитайте целые слова. Короткие имена хороши, но избегайте неоднозначности. -В случае сомнений используйте имя пакета proto до _go с суффиксом pb: +В случае сомнений используйте имя пакета proto до \_go с суффиксом pb: ```go // Хорошо: @@ -751,9 +735,9 @@ import ( В Go [ошибки — это значения (errors are values)]; они создаются кодом и потребляются кодом. Ошибки могут быть: -* Преобразованы в диагностическую информацию для отображения человеку -* Использованы сопровождающим -* Интерпретированы конечным пользователем +- Преобразованы в диагностическую информацию для отображения человеку +- Использованы сопровождающим +- Интерпретированы конечным пользователем Сообщения об ошибках также появляются на самых разных поверхностях, включая сообщения журнала (log messages), дампы ошибок и отрисованные пользовательские @@ -766,35 +750,34 @@ import ( тема, и трудно дать категоричные рекомендации. Используйте свое суждение, но учитывайте следующие соображения: -* Создавая значение ошибки, решите, придавать ли ему какую-либо - [структуру](#error-structure). -* Обрабатывая ошибку, рассмотрите возможность [добавления - информации](#error-extra-info), которая есть у вас, но которой может не быть - у вызывающей и/или вызываемой стороны. -* См. также рекомендации по [логированию ошибок](#error-logging). +- Создавая значение ошибки, решите, придавать ли ему какую-либо + [структуру](#error-structure). +- Обрабатывая ошибку, рассмотрите возможность [добавления + информации](#error-extra-info), которая есть у вас, но которой может не быть + у вызывающей и/или вызываемой стороны. +- См. также рекомендации по [логированию ошибок](#error-logging). Хотя обычно нецелесообразно игнорировать ошибку, разумным исключением из этого является оркестрация связанных операций, где часто только первая ошибка полезна. Пакет [`errgroup`] предоставляет удобную абстракцию для группы операций, которые могут завершиться ошибкой или быть отменены как группа. -[ошибки — это значения (errors are values)]: - https://go.dev/blog/errors-are-values +[ошибки — это значения (errors are values)]: https://go.dev/blog/errors-are-values [`errgroup`]: https://pkg.go.dev/golang.org/x/sync/errgroup См. также: -* [Effective Go об ошибках](https://go.dev/doc/effective_go#errors) -* [Пост в блоге Go об ошибках](https://go.dev/blog/go1.13-errors) -* [Пакет `errors`](https://pkg.go.dev/errors) -* [Пакет - `upspin.io/errors`](https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html) -* [GoTip #89: When to Use Canonical Status Codes as - Errors](https://google.github.io/styleguide/go/index.html#gotip) -* [GoTip #48: Error Sentinel - Values](https://google.github.io/styleguide/go/index.html#gotip) -* [GoTip #13: Designing Errors for - Checking](https://google.github.io/styleguide/go/index.html#gotip) +- [Effective Go об ошибках](https://go.dev/doc/effective_go#errors) +- [Пост в блоге Go об ошибках](https://go.dev/blog/go1.13-errors) +- [Пакет `errors`](https://pkg.go.dev/errors) +- [Пакет + `upspin.io/errors`](https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html) +- [GoTip #89: When to Use Canonical Status Codes as + Errors](/pages/gostyleguide/google/index.html#gotip) +- [GoTip #48: Error Sentinel + Values](/pages/gostyleguide/google/index.html#gotip) +- [GoTip #13: Designing Errors for + Checking](/pages/gostyleguide/google/index.html#gotip) <a id="error-structure"></a> @@ -868,9 +851,9 @@ func handlePet(...) { } ``` -Не пытайтесь различать ошибки на основе их строковой формы. (См. [GoTip #13: +Не пытайтесь различать ошибки на основе их строковой формы. (См. [GoTip #13: Designing Errors for -Checking](https://google.github.io/styleguide/go/index.html#gotip) для получения +Checking](/pages/gostyleguide/google/index.html#gotip) для получения дополнительной информации.) ```go @@ -891,9 +874,9 @@ func handlePet(...) { Могут использоваться и другие структуры ошибок, например, структура проекта, содержащая код ошибки и строку с деталями. [Пакет `status`][status] — распространенная инкапсуляция; если вы выбираете этот подход (вы не обязаны это -делать), используйте [канонические коды (canonical codes)]. См. [GoTip #89: +делать), используйте [канонические коды (canonical codes)]. См. [GoTip #89: When to Use Canonical Status Codes as -Errors](https://google.github.io/styleguide/go/index.html#gotip) чтобы понять, +Errors](/pages/gostyleguide/google/index.html#gotip) чтобы понять, является ли использование кодов статуса правильным выбором. [`os.PathError`]: https://pkg.go.dev/os#PathError @@ -901,8 +884,7 @@ Errors](https://google.github.io/styleguide/go/index.html#gotip) чтобы по [`errors.As`]: https://pkg.go.dev/errors#As [`package cmp`]: https://pkg.go.dev/github.com/google/go-cmp/cmp [status]: https://pkg.go.dev/google.golang.org/grpc/status -[канонические коды (canonical codes)]: - https://pkg.go.dev/google.golang.org/grpc/codes +[канонические коды (canonical codes)]: https://pkg.go.dev/google.golang.org/grpc/codes <a id="error-extra-info"></a> @@ -961,21 +943,20 @@ errors)](https://go.dev/blog/go1.13-errors#whether-to-wrap) с помощью встраивает строковое представление ошибки (то, что возвращает ее метод `Error()`) в новое значение ошибки, отбрасывая любую структурированную информацию из исходной ошибки. Примеры использования `%v`: + - Добавление интересного, не избыточного контекста: как в примере выше. - * Добавление интересного, не избыточного контекста: как в примере выше. - - * Логирование или отображение ошибок: Когда основная цель — представить - удобочитаемое сообщение об ошибке в журналах или пользователю, и вы не - планируете, чтобы вызывающая сторона программно проверяла ошибку с - помощью `errors.Is` или `errors.As` (Примечание: `errors.Unwrap` здесь, - как правило, не рекомендуется, так как он не обрабатывает множественные - ошибки (multi-errors)). + - Логирование или отображение ошибок: Когда основная цель — представить + удобочитаемое сообщение об ошибке в журналах или пользователю, и вы не + планируете, чтобы вызывающая сторона программно проверяла ошибку с + помощью `errors.Is` или `errors.As` (Примечание: `errors.Unwrap` здесь, + как правило, не рекомендуется, так как он не обрабатывает множественные + ошибки (multi-errors)). - * Создание новых, независимых ошибок: Иногда необходимо преобразовать - ошибку в новое сообщение об ошибке, тем самым скрывая специфику исходной - ошибки. Эта практика особенно полезна на границах систем, включая, - помимо прочего, RPC, IPC и хранилища, где мы переводим - доменно-специфичные ошибки в каноническое пространство ошибок. + - Создание новых, независимых ошибок: Иногда необходимо преобразовать + ошибку в новое сообщение об ошибке, тем самым скрывая специфику исходной + ошибки. Эта практика особенно полезна на границах систем, включая, + помимо прочего, RPC, IPC и хранилища, где мы переводим + доменно-специфичные ошибки в каноническое пространство ошибок. ```go // Хорошо: @@ -1008,63 +989,63 @@ errors)](https://go.dev/blog/go1.13-errors#whether-to-wrap) с помощью 1. **`%w` (wrap) для программной проверки и цепочки ошибок (error chaining)** - Глагол `%w` специально предназначен для оборачивания ошибок (error - wrapping). Он создает новую ошибку, которая предоставляет метод `Unwrap()`, - позволяя вызывающим сторонам программно проверять цепочку ошибок с помощью - `errors.Is` и `errors.As`. Примеры использования `%w`: + Глагол `%w` специально предназначен для оборачивания ошибок (error + wrapping). Он создает новую ошибку, которая предоставляет метод `Unwrap()`, + позволяя вызывающим сторонам программно проверять цепочку ошибок с помощью + `errors.Is` и `errors.As`. Примеры использования `%w`: + - Добавление контекста с сохранением исходной ошибки для программной + проверки: Это основной случай использования во вспомогательных функциях + (helpers) вашего приложения. Вы хотите обогатить ошибку дополнительным + контекстом (например, какая операция выполнялась, когда она завершилась + неудачей), но при этом позволить вызывающей стороне проверить, является + ли лежащая в основе ошибка конкретной сторожевой ошибкой или типом. + + ```go + // Хорошо: + func (s *Server) internalFunction(ctx context.Context) error { + // ... + if err != nil { + return fmt.Errorf("couldn't find remote file: %w", err) + } + } + ``` - * Добавление контекста с сохранением исходной ошибки для программной - проверки: Это основной случай использования во вспомогательных функциях - (helpers) вашего приложения. Вы хотите обогатить ошибку дополнительным - контекстом (например, какая операция выполнялась, когда она завершилась - неудачей), но при этом позволить вызывающей стороне проверить, является - ли лежащая в основе ошибка конкретной сторожевой ошибкой или типом. + Это позволяет функции более высокого уровня выполнить `errors.Is(err, - ```go - // Хорошо: - func (s *Server) internalFunction(ctx context.Context) error { - // ... - if err != nil { - return fmt.Errorf("couldn't find remote file: %w", err) - } - } - ``` + fs.ErrNotExist)`, даже если исходная ошибка была обернута. - Это позволяет функции более высокого уровня выполнить `errors.Is(err, - fs.ErrNotExist)`, даже если исходная ошибка была обернута. + В точках, где ваша система взаимодействует с внешними системами, такими + как RPC, IPC или хранилище, часто лучше переводить доменно-специфичные + ошибки в стандартизированное пространство ошибок (например, коды статуса + gRPC), а не просто оборачивать исходную ошибку с помощью `%w`. Клиента + обычно не волнует точная внутренняя ошибка файловой системы; их волнует + канонический результат (например, `Internal`, `NotFound`, + `PermissionDenied`). - В точках, где ваша система взаимодействует с внешними системами, такими - как RPC, IPC или хранилище, часто лучше переводить доменно-специфичные - ошибки в стандартизированное пространство ошибок (например, коды статуса - gRPC), а не просто оборачивать исходную ошибку с помощью `%w`. Клиента - обычно не волнует точная внутренняя ошибка файловой системы; их волнует - канонический результат (например, `Internal`, `NotFound`, - `PermissionDenied`). - - * Когда вы явно документируете и тестируете лежащие в основе ошибки, - которые вы раскрываете: Если API вашего пакета гарантирует, что - определенные лежащие в основе ошибки могут быть развернуты и проверены - вызывающими сторонами (например, "эта функция может вернуть - `ErrInvalidConfig`, обернутый в более общую ошибку"), то `%w` уместен. - Это становится частью контракта вашего пакета. + - Когда вы явно документируете и тестируете лежащие в основе ошибки, + которые вы раскрываете: Если API вашего пакета гарантирует, что + определенные лежащие в основе ошибки могут быть развернуты и проверены + вызывающими сторонами (например, "эта функция может вернуть + `ErrInvalidConfig`, обернутый в более общую ошибку"), то `%w` уместен. + Это становится частью контракта вашего пакета. См. также: -* [Соглашения по документации ошибок (Error Documentation - Conventions)](#documentation-conventions-errors) -* [Пост в блоге об оборачивании ошибок](https://blog.golang.org/go1.13-errors) +- [Соглашения по документации ошибок (Error Documentation + Conventions)](#documentation-conventions-errors) +- [Пост в блоге об оборачивании ошибок](https://blog.golang.org/go1.13-errors) <a id="error-percent-w"></a> ### Размещение `%w` в ошибках (Placement of %w in errors) -Предпочитайте размещать `%w` в конце строки ошибки *если* вы используете +Предпочитайте размещать `%w` в конце строки ошибки _если_ вы используете [оборачивание ошибок (error wrapping)](https://go.dev/blog/go1.13-errors) с глаголом форматирования `%w`. Ошибки могут быть обернуты с помощью глагола `%w` или путем помещения их в [структурированную -ошибку](https://google.github.io/styleguide/go/index.html#gotip), которая +ошибку](/pages/gostyleguide/google/index.html#gotip), которая реализует `Unwrap() error` (например, [`fs.PathError`](https://pkg.go.dev/io/fs#PathError)). @@ -1133,40 +1114,39 @@ fmt.Println(err3) // err3-1 err2-1 err1 err2-2 err3-2 своим вызывающим сторонам. Логирование — очевидный выбор здесь; но будьте внимательны к тому, что и как вы логируете. -* Как и [хорошие сообщения о неудачных тестах (good test failure messages)], - сообщения журнала должны четко выражать, что пошло не так, и помогать - сопровождающему, включая соответствующую информацию для диагностики - проблемы. +- Как и [хорошие сообщения о неудачных тестах (good test failure messages)], + сообщения журнала должны четко выражать, что пошло не так, и помогать + сопровождающему, включая соответствующую информацию для диагностики + проблемы. -* Избегайте дублирования. Если вы возвращаете ошибку, обычно лучше не - логировать ее самостоятельно, а позволить вызывающей стороне обработать ее. - Вызывающая сторона может выбрать логирование ошибки или, возможно, - ограничить частоту логирования с помощью [`rate.Sometimes`]. Другие варианты - включают попытку восстановления или даже [остановку программы]. В любом - случае, предоставление контроля вызывающей стороне помогает избежать спама в - журналах. +- Избегайте дублирования. Если вы возвращаете ошибку, обычно лучше не + логировать ее самостоятельно, а позволить вызывающей стороне обработать ее. + Вызывающая сторона может выбрать логирование ошибки или, возможно, + ограничить частоту логирования с помощью [`rate.Sometimes`]. Другие варианты + включают попытку восстановления или даже [остановку программы]. В любом + случае, предоставление контроля вызывающей стороне помогает избежать спама в + журналах. Однако обратной стороной этого подхода является то, что любое логирование записывается с использованием координат строк вызывающей стороны. -* Будьте осторожны с [PII]. Многие приемники журналов (log sinks) не являются - подходящими местами назначения для конфиденциальной информации конечных - пользователей. +- Будьте осторожны с [PII]. Многие приемники журналов (log sinks) не являются + подходящими местами назначения для конфиденциальной информации конечных + пользователей. -* Используйте `log.Error` скупо. Логирование уровня ERROR вызывает сброс - (flush) и является более дорогостоящим, чем более низкие уровни логирования. - Это может серьезно повлиять на производительность вашего кода. Принимая - решение между уровнями error и warning, учитывайте лучшую практику: - сообщения на уровне error должны быть actionable (то есть требовать - действий), а не просто "более серьезными", чем warning. +- Используйте `log.Error` скупо. Логирование уровня ERROR вызывает сброс + (flush) и является более дорогостоящим, чем более низкие уровни логирования. + Это может серьезно повлиять на производительность вашего кода. Принимая + решение между уровнями error и warning, учитывайте лучшую практику: + сообщения на уровне error должны быть actionable (то есть требовать + действий), а не просто "более серьезными", чем warning. -* Внутри Google у нас есть системы мониторинга, которые можно настроить для - более эффективного оповещения, чем просто запись в файл журнала в надежде, - что кто-то его заметит. Это похоже, но не идентично стандартной библиотеке - [пакету `expvar`]. +- Внутри Google у нас есть системы мониторинга, которые можно настроить для + более эффективного оповещения, чем просто запись в файл журнала в надежде, + что кто-то его заметит. Это похоже, но не идентично стандартной библиотеке + [пакету `expvar`]. -[хорошие сообщения о неудачных тестах (good test failure messages)]: - https://google.github.io/styleguide/go/decisions#useful-test-failures +[хорошие сообщения о неудачных тестах (good test failure messages)]: /pages/gostyleguide/google/decisions#useful-test-failures [остановку программы]: #checks-and-panics [`rate.Sometimes`]: https://pkg.go.dev/golang.org/x/time/rate#Sometimes [PII]: https://en.wikipedia.org/wiki/Personal_data @@ -1180,9 +1160,9 @@ fmt.Println(err3) // err3-1 err2-1 err1 err2-2 err3-2 может быть полезно для разработки и трассировки. Установление соглашения об уровнях детализации может быть полезным. Например: -* Записывайте небольшое количество дополнительной информации на `V(1)` -* Трассируйте больше информации на `V(2)` -* Выводите большие внутренние состояния на `V(3)` +- Записывайте небольшое количество дополнительной информации на `V(1)` +- Трассируйте больше информации на `V(2)` +- Выводите большие внутренние состояния на `V(3)` Чтобы минимизировать стоимость детального логирования, вы должны убедиться, что случайно не вызываете дорогие функции, даже когда `log.V` выключен. `log.V` @@ -1251,8 +1231,7 @@ log.V(2).Infof("Handling %v", sql.Explain()) большие трассировки стека, которые остаются необработанными. Избегайте этой ловушки в своих серверах. -[решении против паник (decision against panics)]: - https://google.github.io/styleguide/go/decisions#dont-panic +[решении против паник (decision against panics)]: /pages/gostyleguide/google/decisions#dont-panic [`net/http` server]: https://pkg.go.dev/net/http#Server <a id="when-to-panic"></a> @@ -1345,14 +1324,13 @@ func answer(i int) string { См. также: -* [Handling panics](https://go.dev/ref/spec#Handling_panics) и [Run-time - Panics](https://go.dev/ref/spec#Run_time_panics) в спецификации языка -* [Defer, Panic, and Recover](https://go.dev/blog/defer-panic-and-recover) -* [On the uses and misuses of panics in - Go](https://eli.thegreenplace.net/2018/on-the-uses-and-misuses-of-panics-in-go/) +- [Handling panics](https://go.dev/ref/spec#Handling_panics) и [Run-time + Panics](https://go.dev/ref/spec#Run_time_panics) в спецификации языка +- [Defer, Panic, and Recover](https://go.dev/blog/defer-panic-and-recover) +- [On the uses and misuses of panics in + Go](https://eli.thegreenplace.net/2018/on-the-uses-and-misuses-of-panics-in-go/) -[Go Tip #81: Avoiding Resource Leaks in API Design]: - https://google.github.io/styleguide/go/index.html#gotip +[Go Tip #81: Avoiding Resource Leaks in API Design]: /pages/gostyleguide/google/index.html#gotip <a id="documentation"></a> @@ -1379,9 +1357,9 @@ func answer(i int) string { Не каждый параметр должен быть перечислен в документации. Это относится к: -* параметрам функций и методов -* полям структур (struct fields) -* API для опций (options) +- параметрам функций и методов +- полям структур (struct fields) +- API для опций (options) Документируйте подверженные ошибкам или неочевидные поля и параметры, объясняя, почему они интересны. @@ -1419,13 +1397,11 @@ func Sprintf(format string, data ...any) string См. также: -* [GoTip #41: Identify Function Call Parameters] -* [GoTip #51: Patterns for Configuration] +- [GoTip #41: Identify Function Call Parameters] +- [GoTip #51: Patterns for Configuration] -[GoTip #41: Identify Function Call Parameters]: - https://google.github.io/styleguide/go/index.html#gotip -[GoTip #51: Patterns for Configuration]: - https://google.github.io/styleguide/go/index.html#gotip +[GoTip #41: Identify Function Call Parameters]: /pages/gostyleguide/google/index.html#gotip +[GoTip #51: Patterns for Configuration]: /pages/gostyleguide/google/index.html#gotip <a id="documentation-conventions-contexts"></a> @@ -1456,7 +1432,7 @@ func (Worker) Run(ctx context.Context) error Когда поведение контекста отличается или неочевидно, его следует прямо задокументировать, если верно любое из следующего. -* Функция возвращает ошибку, отличную от `ctx.Err()`, когда контекст отменен: +- Функция возвращает ошибку, отличную от `ctx.Err()`, когда контекст отменен: ```go // Хорошо: @@ -1466,8 +1442,8 @@ func (Worker) Run(ctx context.Context) error func (Worker) Run(ctx context.Context) error ``` -* Функция имеет другие механизмы, которые могут ее прервать или повлиять на - время жизни: +- Функция имеет другие механизмы, которые могут ее прервать или повлиять на + время жизни: ```go // Хорошо: @@ -1482,8 +1458,8 @@ func (Worker) Run(ctx context.Context) error func (Worker) Stop() ``` -* Функция имеет особые ожидания относительно времени жизни контекста, его - происхождения (lineage) или прикрепленных значений (attached values): +- Функция имеет особые ожидания относительно времени жизни контекста, его + происхождения (lineage) или прикрепленных значений (attached values): ```go // Хорошо: @@ -1536,7 +1512,7 @@ func (*Buffer) Grow(n int) Настоятельно рекомендуется документировать, если верно любое из следующего. -* Непонятно, является ли операция доступной только для чтения или мутирующей: +- Непонятно, является ли операция доступной только для чтения или мутирующей: ```go // Хорошо: @@ -1552,7 +1528,7 @@ func (*Buffer) Grow(n int) состояние LRU-кэша мутирует. Как это реализовано, может быть неочевидно для всех читателей. -* Синхронизация предоставляется API: +- Синхронизация предоставляется API: ```go // Хорошо: @@ -1568,8 +1544,8 @@ func (*Buffer) Grow(n int) **Примечание:** Если API является типом и API предоставляет синхронизацию в целом, по соглашению только определение типа документирует семантику. -* API потребляет пользовательские реализации типов или интерфейсов, и - потребитель интерфейса имеет особые требования к параллелизму: +- API потребляет пользовательские реализации типов или интерфейсов, и + потребитель интерфейса имеет особые требования к параллелизму: ```go // Хорошо: @@ -1632,10 +1608,9 @@ func (c *Client) Get(url string) (resp *Response, err error) См. также: -* [GoTip #110: Don’t Mix Exit With Defer] +- [GoTip #110: Don’t Mix Exit With Defer] -[GoTip #110: Don’t Mix Exit With Defer]: - https://google.github.io/styleguide/go/index.html#gotip +[GoTip #110: Don’t Mix Exit With Defer]: /pages/gostyleguide/google/index.html#gotip <a id="documentation-conventions-errors"></a> @@ -1706,10 +1681,10 @@ package os См. также: -* [Go Tip #106: Error Naming - Conventions](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #89: When to Use Canonical Status Codes as - Errors](https://google.github.io/styleguide/go/index.html#gotip) +- [Go Tip #106: Error Naming + Conventions](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #89: When to Use Canonical Status Codes as + Errors](/pages/gostyleguide/google/index.html#gotip) <a id="documentation-preview"></a> @@ -1729,7 +1704,7 @@ Go имеет [сервер [Godoc] предоставляет специальный синтаксис для [форматирования документации]. -* Требуется пустая строка для разделения абзацев: +- Требуется пустая строка для разделения абзацев: ```go // Хорошо: @@ -1738,8 +1713,8 @@ Go имеет [сервер // См. some/shortlink для подробностей о формате файла конфигурации. ``` -* Файлы тестов могут содержать [запускаемые примеры (runnable examples)], - которые появляются прикрепленными к соответствующей документации в godoc: +- Файлы тестов могут содержать [запускаемые примеры (runnable examples)], + которые появляются прикрепленными к соответствующей документации в godoc: ```go // Хорошо: @@ -1757,8 +1732,8 @@ Go имеет [сервер } ``` -* Отступ строк на два дополнительных пробела форматирует их буквально - (verbatim): +- Отступ строк на два дополнительных пробела форматирует их буквально + (verbatim): ```go // Хорошо: @@ -1786,9 +1761,9 @@ Go имеет [сервер // "env" если присутствует, будет заполнен системным окружением. ``` -* Одна строка, которая начинается с заглавной буквы, не содержит знаков - препинания, кроме скобок и запятых, и за которой следует другой абзац, - форматируется как заголовок: +- Одна строка, которая начинается с заглавной буквы, не содержит знаков + препинания, кроме скобок и запятых, и за которой следует другой абзац, + форматируется как заголовок: ```go // Хорошо: @@ -2004,8 +1979,7 @@ var ( ) ``` -[составные литералы (composite literal)]: - https://golang.org/ref/spec#Composite_literals +[составные литералы (composite literal)]: https://golang.org/ref/spec#Composite_literals <a id="vardeclsize"></a> @@ -2039,12 +2013,11 @@ var ( **Предупреждение:** Предварительное выделение больше памяти, чем нужно, может тратить память в парке (fleet) или даже вредить производительности. В случае -сомнений см. [GoTip #3: Benchmarking Go Code] и по умолчанию используйте +сомнений см. [GoTip #3: Benchmarking Go Code] и по умолчанию используйте [инициализацию нулевым значением](#vardeclzero) или [объявление составным литералом](#vardeclcomposite). -[GoTip #3: Benchmarking Go Code]: - https://google.github.io/styleguide/go/index.html#gotip +[GoTip #3: Benchmarking Go Code]: /pages/gostyleguide/google/index.html#gotip <a id="decl-chan"></a> @@ -2082,8 +2055,7 @@ func sum(values chan int) (out int) { См. также доклад Брайана Миллса "Rethinking Classical Concurrency Patterns": [слайды][rethinking-concurrency-slides] [видео][rethinking-concurrency-video]. -[rethinking-concurrency-slides]: - https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view?usp=sharing +[rethinking-concurrency-slides]: https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view?usp=sharing [rethinking-concurrency-video]: https://www.youtube.com/watch?v=5zXAHh5tJqQ [направление канала (channel direction)]: https://go.dev/ref/spec#Channel_types @@ -2114,7 +2086,7 @@ API, к которым предъявляются более высокие ст механизации (least mechanism)]. См. также: [Go Tip #24: Use Case-Specific -Constructions](https://google.github.io/styleguide/go/index.html#gotip) +Constructions](/pages/gostyleguide/google/index.html#gotip) [структуры опций (option struct)]: #option-structure [вариативных опций (variadic options)]: #variadic-options @@ -2132,14 +2104,14 @@ Constructions](https://google.github.io/styleguide/go/index.html#gotip) Использование структуры опций имеет ряд преимуществ: -* Литерал структуры включает как поля, так и значения для каждого аргумента, - что делает их самодокументированными и затрудняет их перестановку. -* Несущественные или "значения по умолчанию" поля могут быть опущены. -* Вызывающие стороны могут совместно использовать структуру опций и писать - вспомогательные функции для работы с ней. -* Структуры обеспечивают более чистую документацию для каждого поля, чем - аргументы функций. -* Структуры опций могут расти со временем без влияния на места вызова. +- Литерал структуры включает как поля, так и значения для каждого аргумента, + что делает их самодокументированными и затрудняет их перестановку. +- Несущественные или "значения по умолчанию" поля могут быть опущены. +- Вызывающие стороны могут совместно использовать структуру опций и писать + вспомогательные функции для работы с ней. +- Структуры обеспечивают более чистую документацию для каждого поля, чем + аргументы функций. +- Структуры опций могут расти со временем без влияния на места вызова. Вот пример функции, которую можно улучшить: @@ -2199,11 +2171,11 @@ func foo(ctx context.Context) { Этот вариант часто предпочтителен, когда применимо одно из следующих условий: -* Все вызывающие стороны должны указать одну или несколько опций. -* Большому количеству вызывающих сторон необходимо предоставить множество - опций. -* Опции используются совместно несколькими функциями, которые будет вызывать - пользователь. +- Все вызывающие стороны должны указать одну или несколько опций. +- Большому количеству вызывающих сторон необходимо предоставить множество + опций. +- Опции используются совместно несколькими функциями, которые будет вызывать + пользователь. <a id="variadic-options"></a> @@ -2215,20 +2187,19 @@ func foo(ctx context.Context) { опции (если есть), а возвращаемое замыкание принимает изменяемую ссылку (обычно указатель на тип struct), которая будет обновлена на основе входных данных. -[вариативный (`...`) параметр]: - https://golang.org/ref/spec#Passing_arguments_to_..._parameters +[вариативный (`...`) параметр]: https://golang.org/ref/spec#Passing_arguments_to_..._parameters Использование вариативных опций может предоставить ряд преимуществ: -* Опции не занимают места в месте вызова, когда конфигурация не нужна. -* Опции все еще являются значениями, поэтому вызывающие стороны могут делиться - ими, писать вспомогательные функции и накапливать их. -* Опции могут принимать несколько параметров (например, - `cartesian.Translate(dx, dy int) TransformOption`). -* Функции опций могут возвращать именованный тип, чтобы группировать опции - вместе в godoc. -* Пакеты могут разрешать (или запрещать) сторонним пакетам определять (или - запрещать определение) свои собственные опции. +- Опции не занимают места в месте вызова, когда конфигурация не нужна. +- Опции все еще являются значениями, поэтому вызывающие стороны могут делиться + ими, писать вспомогательные функции и накапливать их. +- Опции могут принимать несколько параметров (например, + `cartesian.Translate(dx, dy int) TransformOption`). +- Функции опций могут возвращать именованный тип, чтобы группировать опции + вместе в godoc. +- Пакеты могут разрешать (или запрещать) сторонним пакетам определять (или + запрещать определение) свои собственные опции. **Примечание:** Использование вариативных опций требует значительного количества дополнительного кода (см. следующий пример), поэтому их следует использовать @@ -2327,15 +2298,15 @@ func foo(ctx context.Context) { Предпочитайте этот вариант, когда применимо большинство из следующего: -* Большинству вызывающих сторон не нужно указывать никакие опции. -* Большинство опций используется редко. -* Существует большое количество опций. -* Опции требуют аргументов. -* Опции могут завершиться неудачей или быть установлены неправильно (в этом - случае функция опции возвращает `error`). -* Опции требуют большого количества документации, которую трудно уместить в - структуре. -* Пользователи или другие пакеты могут предоставлять пользовательские опции. +- Большинству вызывающих сторон не нужно указывать никакие опции. +- Большинство опций используется редко. +- Существует большое количество опций. +- Опции требуют аргументов. +- Опции могут завершиться неудачей или быть установлены неправильно (в этом + случае функция опции возвращает `error`). +- Опции требуют большого количества документации, которую трудно уместить в + структуре. +- Пользователи или другие пакеты могут предоставлять пользовательские опции. Опции в этом стиле должны принимать параметры, а не использовать наличие (presence) для сигнализации своего значения; последнее может значительно @@ -2360,10 +2331,8 @@ func foo(ctx context.Context) { См. [оригинальный пост в блоге Роба Пайка] и [доклад Дейва Ченея] для более глубокого изучения того, как эти опции могут быть использованы. -[оригинальный пост в блоге Роба Пайка]: - http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html -[доклад Дейва Ченея]: - https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +[оригинальный пост в блоге Роба Пайка]: http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html +[доклад Дейва Ченея]: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis <a id="complex-clis"></a> @@ -2379,18 +2348,16 @@ func foo(ctx context.Context) { Однако, если вам нужны другие функции, которые она не предоставляет, выберите один из других вариантов. -* **[cobra]** - - * Соглашение о флагах: getopt - * Распространена за пределами кодовой базы Google. - * Много дополнительных функций. - * Подводные камни в использовании (см. ниже). - -* **[subcommands]** +- **[cobra]** + - Соглашение о флагах: getopt + - Распространена за пределами кодовой базы Google. + - Много дополнительных функций. + - Подводные камни в использовании (см. ниже). - * Соглашение о флагах: Go - * Проста и с ней легко работать правильно. - * Рекомендуется, если вам не нужны дополнительные функции. +- **[subcommands]** + - Соглашение о флагах: Go + - Проста и с ней легко работать правильно. + - Рекомендуется, если вам не нужны дополнительные функции. **Предупреждение**: функции команд cobra должны использовать `cmd.Context()` для получения контекста, а не создавать свой собственный корневой контекст с помощью @@ -2423,17 +2390,17 @@ decisions#mark-test-helpers. Цель не в том, чтобы повторя Go различает "тестовые помощники (test helpers)" и "помощники утверждений (assertion helpers)": -* **Тестовые помощники** — это функции, которые выполняют задачи настройки или - очистки. Все сбои, которые происходят в тестовых помощниках, ожидаемо - являются сбоями окружения (а не тестируемого кода) — например, когда - тестовая база данных не может быть запущена, потому что на этой машине - больше нет свободных портов. Для таких функций часто уместно вызывать - `t.Helper`, чтобы [пометить их как тестовый помощник]. См. [обработку ошибок - в тестовых помощниках] для более подробной информации. +- **Тестовые помощники** — это функции, которые выполняют задачи настройки или + очистки. Все сбои, которые происходят в тестовых помощниках, ожидаемо + являются сбоями окружения (а не тестируемого кода) — например, когда + тестовая база данных не может быть запущена, потому что на этой машине + больше нет свободных портов. Для таких функций часто уместно вызывать + `t.Helper`, чтобы [пометить их как тестовый помощник]. См. [обработку ошибок + в тестовых помощниках] для более подробной информации. -* **Помощники утверждений** — это функции, которые проверяют правильность - системы и завершают тест с ошибкой, если ожидание не выполняется. Помощники - утверждений [не считаются идиоматичными] в Go. +- **Помощники утверждений** — это функции, которые проверяют правильность + системы и завершают тест с ошибкой, если ожидание не выполняется. Помощники + утверждений [не считаются идиоматичными] в Go. Цель теста — сообщить о условиях прохождения/непрохождения тестируемого кода. Идеальное место для завершения теста с ошибкой — внутри самой функции `Test`, @@ -2446,7 +2413,7 @@ Go различает "тестовые помощники (test helpers)" и " По мере роста вашего тестового кода может стать необходимым вынести некоторую функциональность в отдельные функции. Стандартные соображения программной -инженерии все еще применяются, поскольку *тестовый код — это все еще код*. Если +инженерии все еще применяются, поскольку _тестовый код — это все еще код_. Если функциональность не взаимодействует с тестовым фреймворком, то применяются все обычные правила. Однако, когда общий код взаимодействует с фреймворком, необходимо соблюдать осторожность, чтобы избежать распространенных подводных @@ -2457,21 +2424,21 @@ Go различает "тестовые помощники (test helpers)" и " организуйте тест одним из следующих способов вместо использования помощников утверждений или сложных функций валидации: -* Встройте логику (и валидацию, и завершение с ошибкой) в функцию `Test`, даже - если это повторяется. Это лучше всего работает в простых случаях. -* Если входные данные похожи, рассмотрите возможность объединения их в - [табличный тест (table-driven test)], сохраняя логику встроенной в цикл. Это - помогает избежать повторения, сохраняя валидацию и завершение с ошибкой в - `Test`. -* Если есть несколько вызывающих сторон, которым нужна одна и та же функция - валидации, но табличные тесты не подходят (обычно потому, что входные данные - недостаточно просты или валидация требуется как часть последовательности - операций), организуйте функцию валидации так, чтобы она возвращала значение - (обычно `error`), а не принимала параметр `testing.T` и использовала его для - завершения теста с ошибкой. Используйте логику внутри `Test`, чтобы решить, - завершать ли тест с ошибкой, и предоставить [полезные сообщения об ошибках - теста]. Вы также можете создать тестовые помощники для выноса общего - шаблонного кода настройки. +- Встройте логику (и валидацию, и завершение с ошибкой) в функцию `Test`, даже + если это повторяется. Это лучше всего работает в простых случаях. +- Если входные данные похожи, рассмотрите возможность объединения их в + [табличный тест (table-driven test)], сохраняя логику встроенной в цикл. Это + помогает избежать повторения, сохраняя валидацию и завершение с ошибкой в + `Test`. +- Если есть несколько вызывающих сторон, которым нужна одна и та же функция + валидации, но табличные тесты не подходят (обычно потому, что входные данные + недостаточно просты или валидация требуется как часть последовательности + операций), организуйте функцию валидации так, чтобы она возвращала значение + (обычно `error`), а не принимала параметр `testing.T` и использовала его для + завершения теста с ошибкой. Используйте логику внутри `Test`, чтобы решить, + завершать ли тест с ошибкой, и предоставить [полезные сообщения об ошибках + теста]. Вы также можете создать тестовые помощники для выноса общего + шаблонного кода настройки. Дизайн, описанный в последнем пункте, сохраняет ортогональность. Например, [пакет `cmp`] не предназначен для завершения тестов с ошибкой, а для сравнения @@ -2581,10 +2548,8 @@ type FS interface { реализацию как черный ящик (blackbox), чтобы убедиться, что она соблюдает самые основные части контракта `io/fs`. -[приемочным тестированием (acceptance testing)]: - https://en.wikipedia.org/wiki/Acceptance_testing -[инверсии управления (inversion of control)]: - https://en.wikipedia.org/wiki/Inversion_of_control +[приемочным тестированием (acceptance testing)]: https://en.wikipedia.org/wiki/Acceptance_testing +[инверсии управления (inversion of control)]: https://en.wikipedia.org/wiki/Inversion_of_control [`io/fs`]: https://pkg.go.dev/io/fs [`testing/fstest`]: https://pkg.go.dev/testing/fstest [`fstest.TestFS`]: https://pkg.go.dev/testing/fstest#TestFS @@ -2619,9 +2584,8 @@ type FS interface { Тест должен отмечать, какие инварианты нарушены и как. Ваш дизайн может выбрать одну из двух дисциплин для сообщения о сбоях: - - * **Завершение при первой ошибке (Fail fast)**: возвращать ошибку, как - только реализация нарушает инвариант. + - **Завершение при первой ошибке (Fail fast)**: возвращать ошибку, как + только реализация нарушает инвариант. Это самый простой подход, и он хорошо работает, если ожидается, что приемочный тест будет выполняться быстро. Простые [сторожевые ошибки @@ -2639,8 +2603,8 @@ type FS interface { } ``` - * **Агрегация всех сбоев (Aggregate all failures)**: собирать все сбои и - сообщать о них всех. + - **Агрегация всех сбоев (Aggregate all failures)**: собирать все сбои и + сообщать о них всех. Этот подход напоминает рекомендацию [продолжать выполнение (keep going)] и может быть предпочтительнее, если ожидается, что приемочный тест будет @@ -2712,10 +2676,9 @@ func TestAcceptance(t *testing.T) { } ``` -[сторожевые ошибки (sentinels)]: - https://google.github.io/styleguide/go/index.html#gotip -[пользовательские типы]: https://google.github.io/styleguide/go/index.html#gotip -[агрегирует ошибки]: https://google.github.io/styleguide/go/index.html#gotip +[сторожевые ошибки (sentinels)]: /pages/gostyleguide/google/index.html#gotip +[пользовательские типы]: /pages/gostyleguide/google/index.html#gotip +[агрегирует ошибки]: /pages/gostyleguide/google/index.html#gotip <a id="use-real-transports"></a> @@ -2734,14 +2697,10 @@ func TestAcceptance(t *testing.T) { double)](https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts) (например, моку, заглушке или фейку) [OperationsServer]. -[тестовому двойнику (test double)]: - https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts -[долго выполняющихся операций (long running operations)]: - https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning -[OperationsClient]: - https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning#OperationsClient -[OperationsServer]: - https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning#OperationsServer +[тестовому двойнику (test double)]: https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts +[долго выполняющихся операций (long running operations)]: https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning +[OperationsClient]: https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning#OperationsClient +[OperationsServer]: https://pkg.go.dev/google.golang.org/genproto/googleapis/longrunning#OperationsServer Это рекомендуется вместо ручной реализации клиента из-за сложности правильной имитации поведения клиента. Используя production-клиент с тестовым сервером, вы @@ -2765,11 +2724,11 @@ double)](https://abseil.io/resources/swe-book/html/ch13.html#basic_concepts) затрагивают одну запись в таблице теста и делают невозможным продолжение работы с этой записью, должны сообщаться следующим образом: -* Если вы не используете подтесты `t.Run`, используйте `t.Error`, за которым - следует оператор `continue` для перехода к следующей записи таблицы. -* Если вы используете подтесты (и вы внутри вызова `t.Run`), используйте - `t.Fatal`, который завершает текущий подтест и позволяет вашему тестовому - случаю перейти к следующему подтесту. +- Если вы не используете подтесты `t.Run`, используйте `t.Error`, за которым + следует оператор `continue` для перехода к следующей записи таблицы. +- Если вы используете подтесты (и вы внутри вызова `t.Run`), используйте + `t.Fatal`, который завершает текущий подтест и позволяет вашему тестовому + случаю перейти к следующему подтесту. **Предупреждение:** Не всегда безопасно вызывать `t.Fatal` и подобные функции. [Подробнее здесь](#t-fatal-goroutine). @@ -2834,8 +2793,8 @@ func addGameAssets(t *testing.T, dir string) error { **Совет:** Go 1.14 представила функцию [`t.Cleanup`], которую можно использовать для регистрации функций очистки, которые запускаются при завершении вашего -теста. Функция также работает с тестовыми помощниками. См. [GoTip #4: Cleaning -Up Your Tests](https://google.github.io/styleguide/go/index.html#gotip) для +теста. Функция также работает с тестовыми помощниками. См. [GoTip #4: Cleaning +Up Your Tests](/pages/gostyleguide/google/index.html#gotip) для рекомендаций по упрощению тестовых помощников. Сниппет ниже в вымышленном файле `paint_test.go` демонстрирует, как @@ -2904,9 +2863,9 @@ FAIL Правильное использование `(*testing.T).Helper` гораздо лучше определяет местоположение сбоя, когда: -* вспомогательные функции растут -* вспомогательные функции вызывают другие вспомогательные функции -* количество использований вспомогательных функций в тестовых функциях растет +- вспомогательные функции растут +- вспомогательные функции вызывают другие вспомогательные функции +- количество использований вспомогательных функций в тестовых функциях растет **Совет:** Если вспомогательная функция вызывает `(*testing.T).Error` или `(*testing.T).Fatal`, предоставьте некоторый контекст в строке формата, чтобы @@ -3122,10 +3081,8 @@ testmain]. Это может произойти, если ресурс, треб [*амортизация общей настройки теста*] или обычного [тестового помощника] для ваших нужд. -[пользовательскую точку входа testmain]: - https://golang.org/pkg/testing/#hdr-Main -[функциональных тестов (functional tests)]: - https://en.wikipedia.org/wiki/Functional_testing +[пользовательскую точку входа testmain]: https://golang.org/pkg/testing/#hdr-Main +[функциональных тестов (functional tests)]: https://en.wikipedia.org/wiki/Functional_testing [*амортизация общей настройки теста*]: #t-setup-amortization [тестового помощника]: #t-common-setup-scope @@ -3186,9 +3143,9 @@ func TestMain(m *testing.M) { Использование `sync.Once` может быть уместным, хотя и не обязательно, если все из следующего верно для общей настройки: -* Она дорогая. -* Она применяется только к некоторым тестам. -* Она не требует очистки. +- Она дорогая. +- Она применяется только к некоторым тестам. +- Она не требует очистки. ```go // Хорошо: @@ -3250,11 +3207,11 @@ func TestRegression682831(t *testing.T) { Есть несколько способов конкатенации строк в Go. Некоторые примеры включают: -* Оператор "+" -* `fmt.Sprintf` -* `strings.Builder` -* `text/template` -* `safehtml/template` +- Оператор "+" +- `fmt.Sprintf` +- `strings.Builder` +- `text/template` +- `safehtml/template` Хотя не существует универсального правила, какой выбрать, следующие рекомендации описывают, когда каждый метод предпочтителен. @@ -3318,8 +3275,8 @@ for i, d := range digitsOfPi { str := b.String() ``` -**Примечание:** Для более подробного обсуждения см. [GoTip #29: Building -Strings Efficiently](https://google.github.io/styleguide/go/index.html#gotip). +**Примечание:** Для более подробного обсуждения см. [GoTip #29: Building +Strings Efficiently](/pages/gostyleguide/google/index.html#gotip). <a id="string-constants"></a> @@ -3362,8 +3319,7 @@ state)](https://en.wikipedia.org/wiki/Global_variable). Им рекоменду критично для поставщиков инфраструктуры, которые предлагают библиотеки, интеграции и сервисы другим командам. -[глобальное состояние (global state)]: - https://en.wikipedia.org/wiki/Global_variable +[глобальное состояние (global state)]: https://en.wikipedia.org/wiki/Global_variable [уровне пакета (package level)]: https://go.dev/ref/spec#TopLevelDecl ```go @@ -3402,18 +3358,18 @@ func main() { См. также: -* [Go Tip #5: Slimming Your Client - Libraries](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #24: Use Case-Specific - Constructions](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #40: Improving Time Testability with Function - Parameters](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #41: Identify Function Call - Parameters](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #44: Improving Time Testability with Struct - Fields](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #80: Dependency Injection - Principles](https://google.github.io/styleguide/go/index.html#gotip) +- [Go Tip #5: Slimming Your Client + Libraries](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #24: Use Case-Specific + Constructions](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #40: Improving Time Testability with Function + Parameters](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #41: Identify Function Call + Parameters](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #44: Improving Time Testability with Struct + Fields](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #80: Dependency Injection + Principles](/pages/gostyleguide/google/index.html#gotip) API, которые не поддерживают явную передачу зависимостей, становятся хрупкими с увеличением числа клиентов: @@ -3480,53 +3436,54 @@ func TestRegression_InvalidUser(t *testing.T) { Использование глобального состояния создает проблемы, на которые нет простых ответов для вас и клиентов API: -* Что произойдет, если клиенту нужно использовать разные и отдельно работающие - наборы `Plugin` (например, для поддержки нескольких серверов) в одном - процессе? +- Что произойдет, если клиенту нужно использовать разные и отдельно работающие + наборы `Plugin` (например, для поддержки нескольких серверов) в одном + процессе? -* Что произойдет, если клиент захочет заменить зарегистрированный `Plugin` - альтернативной реализацией в тесте, например, [тестовым двойником]? +- Что произойдет, если клиент захочет заменить зарегистрированный `Plugin` + альтернативной реализацией в тесте, например, [тестовым двойником]? Что произойдет, если тестам клиента требуется герметичность между экземплярами `Plugin` или между всеми зарегистрированными плагинами? -* Что произойдет, если несколько клиентов `Register` плагин `Plugin` под одним - и тем же именем? Кто победит, если вообще победит? +- Что произойдет, если несколько клиентов `Register` плагин `Plugin` под одним + и тем же именем? Кто победит, если вообще победит? Как следует [обрабатывать](https://neonxp.ru/pages/gostyleguide/google/decisions/#handle-errors) ошибки? Если код вызывает panic или `log.Fatal`, будет ли это всегда [уместно для всех мест, в которых может быть вызван API](https://neonxp.ru/pages/gostyleguide/google/decisions/#dont-panic)? Может ли клиент проверить, что он не делает ничего плохого, прежде чем сделать это? -* Существуют ли определенные этапы начальной загрузки программы или ее - жизненного цикла, во время которых можно вызывать `Register`, а когда нет? +- Существуют ли определенные этапы начальной загрузки программы или ее + жизненного цикла, во время которых можно вызывать `Register`, а когда нет? + + Что произойдет, если `Register` будет вызван в неподходящее время? Клиент + может вызвать `Register` в [`func - Что произойдет, если `Register` будет вызван в неподходящее время? Клиент - может вызвать `Register` в [`func init`](https://go.dev/ref/spec#Package_initialization), до разбора флагов - или после `main`. Этап, на котором вызывается функция, влияет на обработку - ошибок. Если автор API предполагает, что API вызывается *только* во время - инициализации программы без требования, чтобы это было так, это - предположение может подтолкнуть автора к проектированию обработки ошибок для - [завершения программы](https://neonxp.ru/pages/gostyleguide/google/best-practices/#program-init), моделируя API как - функцию типа `Must`. Завершение не подходит для библиотечных функций общего +или после `main`. Этап, на котором вызывается функция, влияет на обработку +ошибок. Если автор API предполагает, что API вызывается _только_ во время +инициализации программы без требования, чтобы это было так, это +предположение может подтолкнуть автора к проектированию обработки ошибок для +[завершения программы](https://neonxp.ru/pages/gostyleguide/google/best-practices/#program-init), моделируя API как +функцию типа `Must`. Завершение не подходит для библиотечных функций общего назначения, которые могут использоваться на любом этапе. -* Что, если потребности в параллелизме клиента и дизайнера не совпадают? +- Что, если потребности в параллелизме клиента и дизайнера не совпадают? См. также: -* [Go Tip #36: Enclosing Package-Level - State](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #71: Reducing Parallel Test - Flakiness](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #80: Dependency Injection - Principles](https://google.github.io/styleguide/go/index.html#gotip) -* Обработка ошибок: [Look Before You - Leap](https://docs.python.org/3/glossary.html#term-LBYL) против [Easier to - Ask for Forgiveness than - Permission](https://docs.python.org/3/glossary.html#term-EAFP) -* [Unit Testing Practices on Public APIs] +- [Go Tip #36: Enclosing Package-Level + State](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #71: Reducing Parallel Test + Flakiness](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #80: Dependency Injection + Principles](/pages/gostyleguide/google/index.html#gotip) +- Обработка ошибок: [Look Before You + Leap](https://docs.python.org/3/glossary.html#term-LBYL) против [Easier to + Ask for Forgiveness than + Permission](https://docs.python.org/3/glossary.html#term-EAFP) +- [Unit Testing Practices on Public APIs] Глобальное состояние имеет каскадные эффекты на [здоровье кодовой базы Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). К глобальному состоянию следует подходить с @@ -3544,7 +3501,7 @@ Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). Ниже перечислены несколько наиболее распространенных проблемных форм API: -* Переменные верхнего уровня, независимо от того, экспортируются они или нет. +- Переменные верхнего уровня, независимо от того, экспортируются они или нет. ```go // Плохо: @@ -3558,14 +3515,14 @@ Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). См. [лакмусовые тесты](#globals-litmus-tests), чтобы узнать, когда они безопасны. -* Шаблон [локатора служб (service locator - pattern)](https://en.wikipedia.org/wiki/Service_locator_pattern). См. - [первый пример](#globals). Сам шаблон локатора служб не является - проблематичным, а проблема в том, что локатор определен как глобальный. +- Шаблон [локатора служб (service locator + pattern)](https://en.wikipedia.org/wiki/Service_locator_pattern). См. + [первый пример](#globals). Сам шаблон локатора служб не является + проблематичным, а проблема в том, что локатор определен как глобальный. -* Реестры для [обратных вызовов - (callbacks)](https://en.wikipedia.org/wiki/Callback_\(computer_programming\)) - и подобного поведения. +- Реестры для [обратных вызовов + (callbacks)](<https://en.wikipedia.org/wiki/Callback_(computer_programming)>) + и подобного поведения. ```go // Плохо: @@ -3578,9 +3535,9 @@ Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). } ``` -* "Толстые" (thick) клиентские синглтоны для таких вещей, как бэкенды, - хранилища, уровни доступа к данным и другие системные ресурсы. Они часто - создают дополнительные проблемы с надежностью служб. +- "Толстые" (thick) клиентские синглтоны для таких вещей, как бэкенды, + хранилища, уровни доступа к данным и другие системные ресурсы. Они часто + создают дополнительные проблемы с надежностью служб. ```go // Плохо: @@ -3598,7 +3555,7 @@ Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). > **Примечание:** Многие устаревшие API в кодовой базе Google не следуют этому > руководству; фактически, некоторые стандартные библиотеки Go позволяют -> настраивать поведение через глобальные значения. Тем не менее, нарушение +> настраивать поведение через глобальные значения. Тем не менее, нарушение > этого руководства устаревшим API **[не должно использоваться как > прецедент](https://neonxp.ru/pages/gostyleguide/google/guide/#local-consistency)** для продолжения шаблона. > @@ -3611,31 +3568,31 @@ Google](https://neonxp.ru/pages/gostyleguide/google/guide/.md#maintainability). [API, использующие шаблоны выше](#globals-forms), небезопасны, когда: -* Несколько функций взаимодействуют через глобальное состояние при выполнении - в одной программе, несмотря на то, что в остальном они независимы (например, - написаны разными авторами в совершенно разных каталогах). -* Независимые тестовые случаи взаимодействуют друг с другом через глобальное - состояние. -* Пользователи API склонны заменять или подменять глобальное состояние для - целей тестирования, особенно чтобы заменить любую часть состояния [тестовым - двойником], например, заглушкой, фейком, шпионом или моком. -* Пользователи должны учитывать особые требования к порядку при взаимодействии - с глобальным состоянием: `func init`, разобраны ли уже флаги и т.д. +- Несколько функций взаимодействуют через глобальное состояние при выполнении + в одной программе, несмотря на то, что в остальном они независимы (например, + написаны разными авторами в совершенно разных каталогах). +- Независимые тестовые случаи взаимодействуют друг с другом через глобальное + состояние. +- Пользователи API склонны заменять или подменять глобальное состояние для + целей тестирования, особенно чтобы заменить любую часть состояния [тестовым + двойником], например, заглушкой, фейком, шпионом или моком. +- Пользователи должны учитывать особые требования к порядку при взаимодействии + с глобальным состоянием: `func init`, разобраны ли уже флаги и т.д. При условии, что вышеуказанные условия избегаются, существует **несколько ограниченных обстоятельств, при которых эти API безопасны**, а именно, когда верно любое из следующего: -* Глобальное состояние логически постоянно - ([пример](https://github.com/klauspost/compress/blob/290f4cfacb3eff892555a491e3eeb569a48665e7/zstd/snappy.go#L413)). -* Наблюдаемое поведение пакета является бессостоятельным (stateless). - Например, общедоступная функция может использовать частную глобальную - переменную в качестве кэша, но пока вызывающая сторона не может отличить - попадания в кэш от промахов, функция является бессостоятельной. -* Глобальное состояние не просачивается в вещи, внешние по отношению к - программе, такие как sidecar-процессы или файлы в общей файловой системе. -* Нет ожидания предсказуемого поведения - ([пример](https://pkg.go.dev/math/rand)). +- Глобальное состояние логически постоянно + ([пример](https://github.com/klauspost/compress/blob/290f4cfacb3eff892555a491e3eeb569a48665e7/zstd/snappy.go#L413)). +- Наблюдаемое поведение пакета является бессостоятельным (stateless). + Например, общедоступная функция может использовать частную глобальную + переменную в качестве кэша, но пока вызывающая сторона не может отличить + попадания в кэш от промахов, функция является бессостоятельной. +- Глобальное состояние не просачивается в вещи, внешние по отношению к + программе, такие как sidecar-процессы или файлы в общей файловой системе. +- Нет ожидания предсказуемого поведения + ([пример](https://pkg.go.dev/math/rand)). > **Примечание:** > [Sidecar-процессы](https://www.oreilly.com/library/view/designing-distributed-systems/9781491983638/ch02.html) @@ -3653,18 +3610,18 @@ image`](https://pkg.go.dev/image) с его функцией лакмусовые тесты, примененные к типичному декодеру, например, для обработки формата [PNG](https://pkg.go.dev/image/png): -* Множественные вызовы API `package image`, использующие зарегистрированные - декодеры (например, `image.Decode`), не могут мешать друг другу, аналогично - и для тестов. Единственное исключение — `image.RegisterFormat`, но это - смягчается пунктами ниже. -* Крайне маловероятно, что пользователь захочет заменить декодер [тестовым - двойником], так как декодер PNG является примером случая, когда предпочтение - нашей кодовой базы реальным объектам применяется. Однако пользователь с - большей вероятностью заменит декодер тестовым двойником, если декодер - состоятельно взаимодействует с ресурсами операционной системы (например, - сетью). -* Коллизии при регистрации возможны, хотя на практике они, вероятно, редки. -* Декодеры являются бессостоятельными, идемпотентными и чистыми (pure). +- Множественные вызовы API `package image`, использующие зарегистрированные + декодеры (например, `image.Decode`), не могут мешать друг другу, аналогично + и для тестов. Единственное исключение — `image.RegisterFormat`, но это + смягчается пунктами ниже. +- Крайне маловероятно, что пользователь захочет заменить декодер [тестовым + двойником], так как декодер PNG является примером случая, когда предпочтение + нашей кодовой базы реальным объектам применяется. Однако пользователь с + большей вероятностью заменит декодер тестовым двойником, если декодер + состоятельно взаимодействует с ресурсами операционной системы (например, + сетью). +- Коллизии при регистрации возможны, хотя на практике они, вероятно, редки. +- Декодеры являются бессостоятельными, идемпотентными и чистыми (pure). <a id="globals-default-instance"></a> @@ -3714,14 +3671,12 @@ image`](https://pkg.go.dev/image) с его функцией состояния к известному хорошему значению по умолчанию (например, для облегчения тестирования). -[целями сборки бинарников (binary build targets)]: - https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/rules.md#go_binary -[библиотеками (libraries)]: - https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/rules.md#go_library +[целями сборки бинарников (binary build targets)]: https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/rules.md#go_binary +[библиотеками (libraries)]: https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/rules.md#go_library См. также: -* [Go Tip #36: Enclosing Package-Level - State](https://google.github.io/styleguide/go/index.html#gotip) -* [Go Tip #80: Dependency Injection - Principles](https://google.github.io/styleguide/go/index.html#gotip) +- [Go Tip #36: Enclosing Package-Level + State](/pages/gostyleguide/google/index.html#gotip) +- [Go Tip #80: Dependency Injection + Principles](/pages/gostyleguide/google/index.html#gotip) |
