Примечания к выпуску Go 1.19

Введение в Go 1.19

Последний выпуск Go, версия 1.19, появился через пять месяцев после Go 1.18. Большинство изменений касаются реализации инструментария, среды выполнения и библиотек. Как и всегда, выпуск сохраняет обещание совместимости Go 1. Ожидается, что почти все программы на Go продолжат компилироваться и выполняться так же, как и раньше.

Изменения в языке

Изменен только один небольшой аспект языка — очень небольшая корректировка к области видимости параметров типа в объявлениях методов. Существующие программы остаются неизменными.

Модель памяти

Модель памяти Go была пересмотрена для согласования с моделью памяти, используемой в C, C++, Java, JavaScript, Rust и Swift. Go предоставляет только последовательно согласованные атомарные операции, но не любые более слабые формы, найденные в других языках. Вместе с обновлением модели памяти, Go 1.19 вводит новые типы в пакете sync/atomic, которые упрощают работу с атомарными значениями, такие как atomic.Int64 и atomic.Pointer[T].

Платформы

LoongArch 64-бит

Go 1.19 добавляет поддержку 64-битной архитектуры Loongson LoongArch в Linux (GOOS=linux, GOARCH=loong64). Реализованная ABI — LP64D. Минимальная поддерживаемая версия ядра — 5.19.

Обратите внимание, что большинство существующих коммерческих дистрибутивов Linux для LoongArch поставляются с более старыми ядрами, с исторически несовместимой ABI системных вызовов. Скомпилированные бинарные файлы не будут работать на этих системах, даже если они статически связаны. Пользователи на таких неподдерживаемых системах ограничены пакетом Go, предоставляемым дистрибутивом.

RISC-V

Порт riscv64 теперь поддерживает передачу аргументов функций и результатов с использованием регистров. Бенчмарки показывают типичное повышение производительности на 10% или более на riscv64.

Инструменты

Комментарии к документации

Go 1.19 добавляет поддержку ссылок, списков и более чётких заголовков в комментариях к документации. В рамках этого изменения gofmt теперь перформатирует комментарии к документации, чтобы их отображаемое значение стало более понятным. См. «Комментарии к документации Go» для синтаксиса и описания распространённых ошибок, теперь выявляемых gofmt. Как ещё один аспект этого изменения, новый пакет go/doc/comment обеспечивает разбор и перформатирование комментариев к документации, а также поддержку отображения их в форматах HTML, Markdown и текст.

Новое условие сборки unix

Условие сборки unix теперь распознаётся в строках //go:build. Условие выполняется, если целевая операционная система, также известная как GOOS, является Unix или Unix-подобной системой. Для выпуска 1.19 условие выполняется, если GOOS равно одному из следующих значений: aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd или solaris. В будущих выпусках условие unix может соответствовать дополнительным вновь поддерживаемым операционным системам.

Команда go

Флаг -trimpath, если он установлен, теперь включается в настройки сборки, помеченные в двоичных файлах Go командой go build, и может быть просмотрен с помощью go version -m или debug.ReadBuildInfo.

Команда go generate теперь явно устанавливает переменную окружения GOROOT в среде генератора, чтобы генераторы могли находить правильный GOROOT, даже если они собраны с флагом -trimpath.

Команды go test и go generate теперь размещают GOROOT/bin в начале переменной PATH, используемой для подпроцесса, так что тесты и генераторы, которые выполняют команду go, будут разрешать её в тот же GOROOT.

Команда go env теперь заключает в кавычки записи, содержащие пробелы в переменных CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS, CGO_LDFLAGS, и GOGCCFLAGS, которые она отображает.

Команда go list -json теперь принимает список полей JSON, разделённых запятыми, для заполнения. Если указан список, вывод JSON будет включать только указанные поля, и команда go list может избежать работы по вычислению полей, которые не включены. В некоторых случаях это может подавлять ошибки, которые иначе были бы сообщены.

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

Vet

Проверка vet “errorsas” теперь сообщает, когда errors.As вызывается со вторым аргументом типа *error, что является распространённой ошибкой.

Среда выполнения

Среда выполнения теперь включает поддержку мягкого ограничения памяти. Это ограничение памяти включает кучу Go и всё остальное управляемое средой выполнения, и исключает внешние источники памяти, такие как отображения самого двоичного файла, память, управляемая на других языках, и память, удерживаемая операционной системой в интересах программы Go. Это ограничение может управляться через runtime/debug.SetMemoryLimit или эквивалентную переменную окружения GOMEMLIMIT. Ограничение работает совместно с runtime/debug.SetGCPercent / GOGC, и будет соблюдаться даже если GOGC=off, позволяя программам Go всегда максимально использовать своё ограничение памяти, улучшая эффективность использования ресурсов в некоторых случаях. См. руководство по GC для подробного объяснения мягкого ограничения памяти, а также различных распространённых сценариев использования и ситуаций. Обратите внимание, что небольшие ограничения памяти, порядка десятков мегабайт или меньше, менее вероятно будут соблюдаться из-за внешних факторов задержки, таких как планирование ОС. См. проблему 52433 для дополнительных деталей. Более крупные ограничения памяти, порядка сотен мегабайт или больше, стабильны и готовы к использованию в продакшене.

Чтобы ограничить влияние thrashing GC, когда размер живой кучи программы приближается к мягкому ограничению памяти, среда выполнения Go также пытается ограничить общее использование CPU GC до 50%, исключая время простоя, отдавая предпочтение использованию большего объёма памяти вместо предотвращения прогресса приложения. На практике мы ожидаем, что это ограничение будет играть роль только в исключительных случаях, и новый метрический показатель среды выполнения /gc/limiter/last-enabled:gc-cycle сообщает, когда это последний раз произошло.

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

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

В операционных системах Unix, Go-программы, импортирующие пакет os, теперь автоматически увеличивают лимит открытых файлов (RLIMIT_NOFILE) до максимального допустимого значения; то есть, они изменяют мягкое ограничение, чтобы оно совпадало с жёстким. Это исправляет искусственно низкие лимиты, установленные на некоторых системах для совместимости с очень старыми C-программами, использующими системный вызов select. Go-программы не получают от этого преимущества, и даже простые программы, такие как gofmt, часто исчерпывают дескрипторы файлов на таких системах при параллельной обработке множества файлов. Одним из последствий этого изменения является то, что Go-программы, которые в свою очередь запускают очень старые C-программы в дочерних процессах, могут запускать эти программы с слишком высоким лимитом. Это можно исправить, установив жёсткий лимит до вызова Go-программы.

Неисправимые фатальные ошибки (например, одновременная запись в карты или разблокировка разблокированного мьютекса) теперь выводят более простой трассировочный стек, исключая метаданные среды выполнения (эквивалентно фатальному панике), если только не установлены параметры GOTRACEBACK=system или crash. Трассировочные стеки фатальных ошибок среды выполнения всегда включают полные метаданные независимо от значения GOTRACEBACK

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

Поддержка address sanitizer, добавленная в Go 1.18, теперь более точно обрабатывает аргументы функций и глобальные переменные.

Компилятор

Компилятор теперь использует таблицу переходов для реализации больших целочисленных и строковых switch-инструкций. Улучшения производительности для switch-инструкции различаются, но могут составлять порядка 20% ускорения. (GOARCH=amd64 и GOARCH=arm64 только)

Компилятор Go теперь требует флаг -p=importpath для построения связываемого объектного файла. Этот флаг уже передаётся командой go и Bazel. Любые другие системы сборки, которые напрямую вызывают компилятор Go, должны убедиться, что они также передают этот флаг.

Компилятор Go больше не принимает флаг -importmap. Системы сборки, которые напрямую вызывают компилятор Go, должны использовать флаг -importcfg вместо него.

Ассемблер

Как и компилятор, ассемблер теперь требует флаг -p=importpath для построения связываемого объектного файла. Этот флаг уже передаётся командой go. Любые другие системы сборки, которые напрямую вызывают ассемблер Go, должны убедиться, что они также передают этот флаг.

Линкер

В платформах ELF линкер теперь выдаёт сжатые секции DWARF в стандартном формате gABI (SHF_COMPRESSED), вместо устаревшего формата .zdebug.

Стандартная библиотека

Новые атомарные типы

Пакет sync/atomic определяет новые атомарные типы: Bool, Int32, Int64, Uint32, Uint64, Uintptr и Pointer. Эти типы скрывают базовые значения, поэтому все обращения вынуждены использовать атомарные API. Pointer также избавляет от необходимости преобразовывать к unsafe.Pointer в местах вызовов. Int64 и Uint64 автоматически выравниваются по 64-битным границам в структурах и выделенных данных, даже на 32-битных системах.

Поиск по PATH

Command и LookPath больше не разрешают находить результаты поиска по PATH относительно текущего каталога. Это устраняет частую причину проблем безопасности, но может также привести к поломке существующих программ, которые зависят от использования, например, exec.Command("prog") для запуска исполняемого файла с именем prog (или, в Windows, prog.exe) в текущем каталоге. См. документацию пакета os/exec для информации о том, как лучше обновить такие программы.

В Windows Command и LookPath теперь уважают переменную окружения NoDefaultCurrentDirectoryInExePath, что позволяет отключить неявный поиск в "." при поиске по PATH в системах Windows.

Незначительные изменения в библиотеке

Как всегда, в библиотеке были внесены различные незначительные изменения и обновления, сделанные с учётом обещания Go 1 совместимости. Также были внесены различные улучшения производительности, которые не перечислены здесь.

archive/zip

Reader теперь игнорирует данные, не являющиеся ZIP, в начале ZIP-файла, соответствует большинству других реализаций. Это необходимо для чтения некоторых Java JAR-файлов, а также других применений.

crypto/elliptic

Работа с недопустимыми точками кривой (такими, для которых метод IsOnCurve возвращает false, и которые никогда не возвращаются методом Unmarshal или методом Curve, работающим с допустимой точкой) всегда была неопределённым поведением и может привести к атакам на восстановление ключа. Если недопустимая точка передаётся в Marshal, MarshalCompressed, Add, Double или ScalarMult, то теперь они будут вызывать панику.

Операции ScalarBaseMult на кривых P224, P384 и P521 теперь на три раза быстрее, что приводит к аналогичным ускорениям в некоторых операциях ECDSA. Общая (не оптимизированная под конкретную платформу) реализация P256 была заменена на реализацию, полученная из формально верифицированной модели; это может привести к значительному замедлению на 32-битных платформах.

crypto/rand

Функция Read больше не буферизует случайные данные, полученные из операционной системы между вызовами. Приложения, выполняющие множество мелких чтений с высокой частотой, могут выбрать wrapping Reader с помощью bufio.Reader по соображениям производительности, при этом следует использовать io.ReadFull для предотвращения частичного чтения.

В системе Plan 9 реализация функции Read была переписана, алгоритм ANSI X9.31 заменён на быстрый генератор стирания ключей.

Реализация Prime была изменена для использования только метода отбрасывания (rejection sampling), что устраняет предвзятость при генерации малых простых чисел в некриптографических контекстах, удаляет один возможный незначительный утечку времени выполнения, а также лучше согласует поведение с BoringSSL, при этом упрощая реализацию. Изменение приводит к другим результатам для заданного потока случайных данных по сравнению с предыдущей реализацией, что может сломать тесты, написанные с ожиданием конкретных результатов из конкретных детерминированных случайных источников. Чтобы предотвратить подобные проблемы в будущем, реализация теперь намеренно недетерминирована относительно входного потока.

crypto/tls

Опция GODEBUG tls10default=1 была удалена. Всё ещё возможно включить TLS 1.0 на стороне клиента, установив Config.MinVersion.

Сервер и клиент TLS теперь отклоняют дублирующиеся расширения в TLS рукопожатиях, как требуется RFC 5246, раздел 7.4.1.4 и RFC 8446, раздел 4.2.

crypto/x509

CreateCertificate больше не поддерживает создание сертификатов с SignatureAlgorithm установленным в MD5WithRSA.

CreateCertificate больше не принимает отрицательные номера сертификатов.

CreateCertificate больше не будет выдавать пустую SEQUENCE, когда созданный сертификат не имеет расширений.

Удаление опции GODEBUG x509sha1=1, первоначально запланированной для Go 1.19, было перенесено на будущий выпуск. Приложения, использующие её, должны перейти на новую реализацию. Практические атаки против SHA-1 были продемонстрированы с 2017 года, а доверенные центры сертификации больше не выдавали сертификаты SHA-1 с 2015 года.

ParseCertificate и ParseCertificateRequest теперь отклоняют сертификаты и запросы сертификатов, содержащие дублирующиеся расширения.

Новые методы CertPool.Clone и CertPool.Equal позволяют клонировать CertPool и проверить эквивалентность двух CertPool соответственно.

Новая функция ParseRevocationList предоставляет более быстрый и безопасный парсер CRL, возвращающий RevocationList. Парсинг CRL также заполняет новые поля RevocationList RawIssuer, Signature, AuthorityKeyId и Extensions, которые игнорируются CreateRevocationList.

Новый метод RevocationList.CheckSignatureFrom проверяет, что подпись в CRL является действительной подписью от Сертификата.

Функции ParseCRL и ParseDERCRL теперь устарели в пользу ParseRevocationList. Метод Certificate.CheckCRLSignature устарел в пользу RevocationList.CheckSignatureFrom.

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

crypto/x509/pkix

Типы CertificateList и TBSCertificateList устарели. Вместо них следует использовать новую функциональность CRL в crypto/x509.

debug/elf

Новые константы EM_LOONGARCH и R_LARCH_* поддерживают порт loong64.

debug/pe

Новый метод File.COFFSymbolReadSectionDefAux, возвращающий COFFSymbolAuxFormat5, предоставляет доступ к информации COMDAT в разделах файлов PE. Поддержка новых констант IMAGE_COMDAT_* и IMAGE_SCN_*.

encoding/binary

Новый интерфейс AppendByteOrder предоставляет эффективные методы для добавления uint16, uint32 или uint64 в байтовый срез. BigEndian и LittleEndian теперь реализуют этот интерфейс.

Аналогично, новые функции AppendUvarint и AppendVarint являются эффективными версиями PutUvarint и PutVarint.

encoding/csv

Новый метод Reader.InputOffset сообщает текущую позицию входных данных как смещение в байтах, аналогично encoding/json’s Decoder.InputOffset.

encoding/xml

Новый метод Decoder.InputPos сообщает текущую позицию входных данных как номер строки и столбца, аналогично encoding/csv’s Decoder.FieldPos.

flag

Новая функция TextVar определяет флаг с значением, реализующим encoding.TextUnmarshaler, что позволяет переменным флагов командной строки иметь типы, такие как big.Int, netip.Addr и time.Time.

fmt

Новые функции Append, Appendf и Appendln добавляют отформатированные данные в байтовые срезы.

go/parser

Парсер теперь распознаёт ~x как унарное выражение с оператором token.TILDE, что позволяет улучшить восстановление после ошибок при использовании ограничений типов, таких как ~int, в некорректном контексте.

go/types

Новые методы Func.Origin и Var.Origin возвращают соответствующий Object для генерик-типа для синтетических Func и Var объектов, созданных во время инстанцирования типа.

Больше невозможно создать бесконечное количество различных, но идентичных Named инстанциаций типа через рекурсивные вызовы Named.Underlying или Named.Method.

hash/maphash

Новые функции Bytes и String обеспечивают эффективный способ хеширования одного байтового среза или строки. Они эквивалентны использованию более общего Hash с единственной записью, но избегают накладных расходов на инициализацию для небольших входных данных.

html/template

Тип FuncMap теперь является псевдонимом для text/template’s FuncMap вместо собственного именованного типа. Это позволяет писать код, который работает с FuncMap из любого из этих пакетов.

Go 1.19.8 и более поздние версии запрещают действия в литералах шаблонов ECMAScript 6. Это поведение может быть отменено с помощью настройки GODEBUG=jstmpllitinterp=1.

image/draw

Draw с оператором Src сохраняет цвета с непредварительно умноженным альфа-каналом, когда и целевое, и исходное изображения являются image.NRGBA или оба image.NRGBA64. Это отменяет изменение поведения, случайно внесенное оптимизацией библиотеки Go 1.18; теперь код соответствует поведению Go 1.17 и более ранних версий.

io

Результат функции NopCloser теперь реализует интерфейс WriterTo в случае, если его входной параметр реализует этот интерфейс.

Результат функции MultiReader теперь реализует интерфейс WriterTo безусловно. Если какой-либо из базовых читателей не реализует интерфейс WriterTo, то соответствующая эмуляция будет выполнена должным образом.

mime

В Windows только, пакет mime теперь игнорирует запись в реестре, которая указывает, что расширение .js должно иметь MIME-тип text/plain. Это распространённая непреднамеренная ошибка конфигурации в системах Windows. В результате, .js будет иметь стандартный MIME-тип text/javascript; charset=utf-8. Приложения, которые ожидают получения text/plain в Windows, должны теперь явно вызывать AddExtensionType.

mime/multipart

Начиная с Go 1.19.8 и выше, этот пакет устанавливает ограничения на размер обрабатываемых MIME-данных для защиты от вредоносных входных данных. Reader.NextPart и Reader.NextRawPart ограничивают количество заголовков в части до 10000, а Reader.ReadForm ограничивает общее количество заголовков во всех FileHeaders до 10000. Эти ограничения могут быть изменены с помощью параметра GODEBUG=multipartmaxheaders. Reader.ReadForm дополнительно ограничивает количество частей в форме до 1000. Это ограничение может быть изменено с помощью параметра GODEBUG=multipartmaxparts.

net

Чисто Go-резолвер теперь будет использовать EDNS(0) для включения предлагаемой максимальной длины ответного пакета, позволяя ответным пакетам содержать до 1232 байт (предыдущий максимум был 512). В маловероятном случае, если это вызовет проблемы с локальным DNS-резолвером, установка переменной окружения GODEBUG=netdns=cgo для использования резолвера на основе cgo должна помочь. Просьба сообщать о подобных проблемах на трекере ошибок.

Когда функция или метод пакета net возвращает ошибку "I/O timeout", ошибка теперь удовлетворяет условию errors.Is(err, context.DeadlineExceeded). Когда функция пакета net возвращает ошибку "operation was canceled", ошибка теперь удовлетворяет условию errors.Is(err, context.Canceled). Эти изменения направлены на упрощение тестирования случаев, когда отмена контекста или истечение времени ожидания приводят к возврату ошибки из функции или метода пакета net, при этом сохраняется обратная совместимость сообщений об ошибках.

Resolver.PreferGo теперь реализован в Windows и Plan 9. Ранее он работал только на Unix-платформах. В сочетании с Dialer.Resolver и Resolver.Dial, теперь возможно создавать портируемые программы и контролировать все DNS-запросы при установке соединения.

Пакет net теперь имеет начальную поддержку тега сборки netgo в Windows. При использовании, пакет использует клиент DNS на Go (как и Resolver.PreferGo), вместо того чтобы запрашивать результаты DNS у Windows. Однако, DNS-сервер, определённый Windows, может быть некорректным при сложных конфигурациях сетей.

net/http

ResponseWriter.WriteHeader теперь поддерживает отправку пользовательских 1xx информационных заголовков.

io.ReadCloser, возвращаемый MaxBytesReader, теперь возвращает определённый тип ошибки MaxBytesError при превышении лимита чтения.

HTTP клиент будет обрабатывать 3xx ответ без заголовка Location, возвращая его вызывающей стороне, вместо того чтобы рассматривать его как ошибку.

net/url

Новая функция JoinPath и метод URL.JoinPath создают новый URL путём объединения списка элементов пути.

Тип URL теперь различает URLs без авторизации и URLs с пустой авторизацией. Например, http:///path имеет пустую авторизацию (хост), в то время как http:/path не имеет её.

Новое поле URL OmitHost устанавливается в true, если URL имеет пустую авторизацию.

os/exec

Cmd с непустым полем Dir и nil Env теперь неявно устанавливает переменную окружения PWD для подпроцесса, чтобы она соответствовала Dir.

Новый метод Cmd.Environ сообщает окружение, которое будет использовано для запуска команды, включая неявно установленную переменную PWD.

reflect

Метод Value.Bytes теперь принимает адресуемые массивы в дополнение к срезам.

Методы Value.Len и Value.Cap теперь успешно работают с указателем на массив и возвращают длину этого массива, соответствующую тому, что делают встроенные функции len и cap.

regexp/syntax

Выпуск Go 1.18 release candidate 1, Go 1.17.8 и Go 1.16.15 включали исправление безопасности в парсере регулярных выражений, которое заставляет его отклонять чрезмерно глубоко вложенные выражения. Поскольку патч-релизы Go не вводят новый API, парсер возвращал syntax.ErrInternalError в этом случае. Go 1.19 добавляет более конкретную ошибку, syntax.ErrNestingDepth, которую парсер теперь возвращает вместо предыдущей.

runtime

Функция GOROOT теперь возвращает пустую строку (вместо "go"), когда бинарный файл был собран с флагом -trimpath и переменная GOROOT не установлена в среде выполнения.

runtime/metrics

Новая /sched/gomaxprocs:threads метрика сообщает текущее значение runtime.GOMAXPROCS.

Новая метрика /cgo/go-to-c-calls:calls metric сообщает о общем количестве вызовов, выполненных из Go в C. Эта метрика идентична функции runtime.NumCgoCall.

Новая метрика /gc/limiter/last-enabled:gc-cycle metric сообщает о последнем цикле GC, когда лимитер CPU GC был включен. См. примечания о runtime для получения подробной информации о лимитере CPU GC.

runtime/pprof

Времена пауз "Stop-the-world" были значительно сокращены при сборе профилей горутин, что уменьшает общее влияние задержек на приложение.

MaxRSS теперь указывается в профилях кучи для всех операционных систем Unix (ранее она отображалась только для GOOS=android, darwin, ios, и linux).

runtime/race

Детектор гонок был обновлён для использования версии thread sanitizer v3 на всех поддерживаемых платформах кроме windows/amd64 и openbsd/amd64, которые остаются на v2. По сравнению с v2, он теперь обычно в 1.5–2 раза быстрее, использует вдвое меньше памяти и поддерживает неограниченное количество горутин. На Linux, детектор гонок теперь требует как минимум версию glibc 2.17 и GNU binutils 2.26.

Детектор гонок теперь поддерживается на GOARCH=s390x.

Поддержка детектора гонок для openbsd/amd64 была удалена из thread sanitizer upstream, поэтому, вероятно, никогда не будет обновлена с v2.

runtime/trace

При трассировке и одновременном включении CPU профайлера в выполнении трассировки включаются выборки CPU профайлера как мгновенные события.

sort

Алгоритм сортировки был переписан с использованием pattern-defeating quicksort, который работает быстрее в нескольких распространённых сценариях.

Новая функция Find похожа на Search, но часто проще использовать: она возвращает дополнительный булевый тип, указывающий, найдено ли равное значение.

strconv

Quote и связанные функции теперь заключают символ U+007F в кавычки как \x7f, а не \u007f, для согласованности с другими ASCII значениями.

syscall

На PowerPC (GOARCH=ppc64, ppc64le), Syscall, Syscall6, RawSyscall, и RawSyscall6 теперь всегда возвращают 0 для значения возврата r2, вместо неопределенного значения.

На AIX и Solaris теперь определена функция Getrusage.

time

Новый метод Duration.Abs обеспечивает удобный и безопасный способ получения абсолютного значения продолжительности, преобразуя −2⁶³ в 2⁶³−1. (Этот граничный случай может возникнуть в результате вычитания недавнего времени из нулевого времени.)

Новый метод Time.ZoneBounds возвращает начальное и конечное время часового пояса, действующего в заданное время. Он может использоваться в цикле для перечисления всех известных переходов часовых поясов в заданной локации.

GoRu.dev Golang на русском

На сайте представлена адаптированная под русский язык документация языка программирования Golang