Go Update
3.11K subscribers
8 photos
74 links
Канал про новости связанные с языком программирования Go. Эволюция языка, стандартной библиотеки и просто интересные вещи над которыми работает Go Core Team и не только.

Админ: @lepage_d
Download Telegram
А ещё планируем с Колей записать подкаст по поводу релиза 1.25 где у вас будет возможность задать все интересующие вас вопросы. Вероятно объяснение нового сборщика мусора будет в первую очередь там (с помощью пальцев и активной жестикуляции).

Так что следите за анонсами 😁.

Статья здесь все равно будет, если вы не переносите видео.
17🔥10😐1
13го августа в 22:40 по Москве в канале ничего не происходить 😁.
😁307👍2
🥂Что нового в Go v1.25 — подробно обсуждаем новую версию языка

https://youtube.com/live/VHjXHzs742c?feature=share

Когда: в ближайшую субботу, 23 августа, 11:00 по Мск

Обсуждаем тем же составом, которым обсуждали когда-то Go v1.21:
- Николай Тузов
- Глеб Яльчик
- Дмтрий Матрёничев

А пока ждёте, советую почитать посты Димы на эту тему, у него очень подробные и крутые разборы

#gogetpodcast #go_1_25
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7
Я только сейчас понял, что у нас снова, за долгое время, совпали цифры релиза и года. Причем впервые совпали обе цифры. В последний раз это был Go 1.6 и совпала только последняя.

Таймлайн:

go1.0 2012-03-28
go1.1 2013-05-13
go1.2 2013-12-01
go1.3 2014-06-18
!go1.4 2014-12-10
!go1.5 2015-08-19
!go1.6 2016-02-17
go1.7 2016-08-15
go1.8 2017-02-16
go1.9 2017-08-24
go1.10 2018-02-16
go1.11 2018-08-24
go1.12 2019-02-25
go1.13 2019-09-03
go1.14 2020-02-25
go1.15 2020-08-11
go1.16 2021-02-16
go1.17 2021-08-16
go1.18 2022-03-15
go1.19 2022-08-02
go1.20 2023-02-01
go1.21 2023-08-08
go1.22 2024-02-06
go1.23 2024-08-13
go1.24 2025-02-11
!go1.25 2025-08-12
🔥21👍6🤯5💩2
👁Визуализатор нашего трехцветного сборщика мусора.

Сегодня на подкасте я упомянул, что у нас трехцветный сборщик мусора. Для меня, как человека, который привык разбирать вещи «на практике или в действии» самым лучшим объяснением выступил визуализатор от ребят из Pusher который они сделали в рамках своей своей статьи про его особенности. И хотя статье уже почти 8 лет, ничего из основ, которые в нее положены, с тех пор не поменялось. В общем: очень и очень рекомендую.

Так-же есть хорошая статья про сборщик мусора от Авито (хотя секция про Write Barrier довольно тяжела для восприятия) и цикл статей от Ardan Labs о основах памяти и о том как правильно читать выводы рантайма про сборку мусора.
🔥29
🚀 Расширение функции new для создания указателя на значения 🚀

Отличные новости! Предложение, обсуждение которого которого длится уже больше четырех лет и которое выдвинул сам Роб Пайк, наконец-то подходит к принятию приняли!

В чем суть: есть у нас встроенная функция new, которая принимает тип и возвращает указатель на значение этого типа. Хорошая функция, однако исторически так сложилось, что она была в тени оператора "&" который использовался и для создания указателей на комплексные типы и для взятия адреса существующих переменных. С учетом того, что для создания словарей, каналов и срезов используется функция make, прикладного постоянного использования у new было немного.

Однако у оператора "&" тоже есть недостатки. Самый явный — он не умеет работать со значениями примитивов (т.е. нельзя сделать a := &1, такой код просто не скомпилируется). Другая проблема в том, что для продолжения работы с указателем, в месте его взятия, нам нужно помещать выражение в скобочки. Т.е. нельзя написать &myComplexType{}.CallStuff() но можно написать (&myComplexType{}).CallStuff().

И вот теперь, спустя 13 лет после релиза языка, нас ждет камбэк функции new так как ее новый синтаксис будет принимать как типы, так и значения. Текущее предложение делает корректным вот такий синтаксис:


a := new(123)
b := new(myConstant)
c := new(int64(-123))



А сие значит, что вероятно точно уже в 1.26 можно будет избавится от хелперов, таких как:

func ptrTo[V any](v V) *V { return &v }


Маленькое, но давно назревшее изменение, принятию которого (пускай и в слегка измененной версии) рад даже сам Пайк.
46🔥28👍18
Тут появилась запись нашего подкаста где мы обсуждали 1.25. Получилось довольно неплохо, как и говорил — поводил руками при объяснении нового сборщика мусора. Ну и про другие вещи не забыли.
2👍2🔥1
🥂Выпуск про Go 1.25 уже доступен / GoGetPodcast 17

https://youtu.be/fHuJNsZPCJ0

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

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

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

- Досмотривайте выпуски до конца
- Подписывайтесь на канал
- Ставьте лайки
- Делитесь с друзьями и коллегами

Это правда очень важно.

🫶 Чем быстрее растёт аудитория подкаста, тем чаще будут выходить новые выпуски, и тем больше я буду вкладываться в их качество.

Несмотря на скромные показатели, я всё же возобновил регулярные выпуски, как вы могли заметить. Потому что хороших подкастов по Go сейчас практически нет, и кто-то ведь должен этим заниматься 😩
Очень надеюсь на вашу поддержку.

#gogetpodcast #news
Please open Telegram to view this post
VIEW IN TELEGRAM
11🔥7👍2
🏗️ gogrep — инструмент для семантического поиска внутри вашей кодовой базы. 🏗️

Наверняка много кому приходилось сталкиваться с ситуацией «надо по коду найти вызовы по определенному паттерну». Чаще всего для этого мы используем регулярные выражения (grep, ag, rg). Однако у них всех есть один минус — они интерпретируют файлы как текст, а не как код. Из-за этого в результаты поиска попадают как нужные нам места вызова, так и комментарии, участки текста и прочая.

Решение этой проблемы: семантический поиск. Это когда утилита разбивает файл на синтаксическое дерево и производит поиск уже по самому дереву. Приведу простой пример:

~/mws/api > gogrep . 'ptr.Get($_)'


Здесь мы явно говорим: найди все вызовы, у которых слева есть идентификатор ptr а внутри вызова идет только один аргумент (не важно выражение, их сумма, или переменная). Таким образом мы идентифицируем именно конкретные места которые будут частью компилируемого кода.

Документация по gogrep доступна тут. С описанием синтаксиса немного сложнее: большую (и лучшую) часть информации по мэтчингу и по фильтрам можно найти в тестах. Сама тулза является часть куда более мощной тулзы go-ruleguard (которая кстати входит в golangci-lint).

За обе утилиты огромнейшее спасибо Искандеру Шарипову.
14👍6
Go Update
🏗️ gogrep — инструмент для семантического поиска внутри вашей кодовой базы. 🏗️ Наверняка много кому приходилось сталкиваться с ситуацией «надо по коду найти вызовы по определенному паттерну». Чаще всего для этого мы используем регулярные выражения (grep,…
Тут мне справедливо заметили, что есть и другие утилиты подобного толка, которые и умеют больше и языков поддержка у них шире. Соглашусь. Для исторической правды приведу те, которые известны лично мне:

semgrep — один из старейших инструментов статического анализа. Поддерживает больше 30 языков, имеет GUI, поддерживает файл с комплексными правилами и вообще комбайн. Недостаток только один: для использования некоторого функционала нужна коммерческая лицензия. Рекомендую глянуть набор правил (часть написана на ruleguard о котором ниже) от Дэмиена Граски для понимания всех возможностей которые может данная утилита.
opengrep — после того как ребята ответственные с утилиту из прошлого пункта решили переехать на коммерческие рельсы, коллектив из нескольких AppSec команд форкнул проект и продолжил развивать его самостоятельно. Умеет почти все (и даже больше), что умеет semgrep, и весь функционал абсолютно бесплатный.
ast-grep — относительный новичок в поле поиска по паттерну в AST. Написан на Rust. Поддерживает трансформацию кода и меньше, чем прошлые две утилиты, ориентирован на поиск проблем с безопастностью.

При всех плюсах вышестоящих утилит, для меня главный их минус в их универсальности. Для каждой из них Go не является основным языком, что ограничивает мощности паттернов по поиску и трансформации. Более того, ruleguard (который частично/полностью основан на gogrep) поддерживает возможность написания трансформаторов кода (автоматический рефакторинг) на Go, а значит не нужно учить и запоминать еще один синтаксис. Плюс из коробки у нас интеграция с go/build, go/ast, go/tokens и go/types что позволяет получить всю мощь Go инструментария не выходя за пределы утилиты.
👍9🔥31👏1
Go Update
Опросы в канале, да или нет? Там ко мне пришли с рекламой с предложением разместить опрос. Я обычно никакие опросы никогда сам не прохожу (кроме Go Developer Survey), поэтому оставляю решение за аудиторией. Как проголосуете, так и будем делать. П.С. Рекламы…
Есть такая старинная мудрость, о которой я регулярно забываю: «Никогда не говори Никогда». Ибо приходиться потом стыковать неидеальную жизнь с идеалистичными заявлениями. Тем более я никогда не думал, что мой бред мои очерки будет читать почти три тысячи человек.

За более чем два года, с момента цитируемого сообщения, ко мне приходило достаточно большое число людей (и гораздо больше чем я вообще ожидал) с разными предложениями: от продажи канала, до интеграций с разными площадками. Большинство (включая продажу) я отклоняю сразу без вопросов — если бы я делал блог на продажу, я бы не делал его таким личным. Большинство рекламных сообщений я так-же отклоняю, потому как считаю неразумным размещать вещи в которых я не уверен, ради относительно небольшого дохода. Сюда же идёт любой инструментарий для автоматического размещения рекламы. Да и текущая активность канала приведет к тому, что доля рекламы быстро приблизиться к негуманным «один-к-одному» и читать станет невозможно. Любые интеграции я оставляю на подумать, но мне никогда не хватает времени, что-бы к ним потом вернуться 😁️️️️️️.

Что подводит нас к маленькому, но важному: есть небольшой процент вещей, за которые я готов вписаться или которые мне интересны, и которые я готов тут размещать. При обязательном условии, что они попадают под тематику канала. Более того, технически я уже нарушал своё же правило: когда размещал объявления о своих выступления на других конференциях и/или подкастах.

Поэтому, в целях прозрачности, с сегодняшнего дня я думаю немного изменить вышестоящее правило:

1. Я буду рассказывать про те вещи (конференции, подкасты, митапы, курсы и прочее) за которые я готов «вписаться». Сюда входят мероприятия, в которых либо я сам принимаю участие (хоть и не организовываю), либо организаторы являются людьми в которых я уверен.
2. Я так-же возможно буду рассказывать про вещи которые мне интересны и могут быть интересны читателям. Сюда идут конференции и инструменты про которые я либо знаю-слышал, либо знаю «через кого-то» и они обладают достаточной степенью доверия, но вписаться за кого я дать не готов.

«А зачем что-то менять?» последует логичный вопрос. На мой взгляд существует ряд вещей которые находятся на стыке с тематикой этого канала и которые могут быть потенциально интересны тем кто меня читает.

Притом ко всем записям по прежнему останутся открытые комментарии: если пойдет какой-то откровенный шлак, то об этом всегда можно будет написать своё «фи». Но я очень надеюсь, что до этого не дойдёт, ибо меня сильно радует, что более чем у 20 процентов моих подписчиков включены уведомления о новом потоке бреда сообщении в канале.
👍36😐1
✔️ errors.AsType — типобезопастная замена errors.As ✔️

Тем временем, в 1.26 нас (вероятно) ждет еще одно приятное изменение: дженерики наконец доберутся до пакета errors.

Все изменение проще описать тремя строчками кода. В 1.25 у нас вот так:


var pe *fs.PathError
if errors.As(err, &pe) {
fmt.Println("Failed at path:", pe.Path)
}


А в 1.26 можно будет вот так:


if pe, ok := errors.AsType[*fs.PathError](err); ok {
fmt.Println("Failed at path:", pe.Path)
}


Вроде и небольшое изменение, но оно ведет, как минимум, к двум положительным вещам:
• Зона видимости типизированной ошибки во многих участках у нас теперь будет меньше, а значит меньше захламляется пространство имен и снижается необходимость думать над правильным именем для ошибки.
• В отличии от errors.As, который вторым аргументом принимал any, новая функция принимает только тех, кто реализует интерфейс error. Несмотря на то, что у нас есть проверка внутри go vet проверяющая второй аргумент у As, всегда приятнее когда компилятор может самостоятельно поймать ошибку на этапе сборки приложения.

Кстати, причина по которой сигнатура текущей функции выглядит как As(err error, target any) bool заключается в том, что указатель на интерфейс и указатель на тип реализующий интерфейс для компилятора две несовместимые конструкции. Иначе говоря, вот такой код


func As(err error, target *error) bool {
panic("unimplemented")
}



pe *fs.PathError
if As(err, &pathError) {
fmt.Println("Failed at path:", pathError.Path)
}





компиляцию не пройдет. А причина в том, что интерфейсы у нас это отдельная сущность которая существует не только во время компиляции, но и во время выполнения.
👍24🤮7🔥41
Я тут выступать подписался на 14ую Стачку в Питере. 2го октября на сей конференции, я буду рассказывать про интересное:

Итераторы: не опять, а снова.

Расскажу про дизайн и эволюцию итераторов. Начну с того, как про них писали в "Паттернах проектирования" и какими были итераторы в Go 1.0. Как разработчики пришли к текущему дизайну "range-over-func", какие проблемы пришлось решить (почему переделали циклы for). Почему большинство Go разработчиков уже пишет итераторы, но еще не знает об этом. Расскажу про то, как это работает под капотом и насколько все хорошо/плохо с производительностью, почему итераторы это не только про скорость. И в заключении расскажу о том, как мы используем итераторы в MWS Cloud Platform и где их использовать не стоит.


А еще мне как спикеру выдали промокод GOPHER10 - дающий скидку 10% на покупку билета. В общем если раздумывали о том, пойти или нет, то у вас есть еще один аргумент за.

П.С. Сентябрь дрянь. Квартира — инфекционный бокс уже третью неделю. Надеюсь, что дальше будет лучше.
🔥222
Go Update
❄️ runtime/secret: add new package: о тех кто застрял в лимбе. Как часто вы зануляете память? Подозреваю, что ответ большинства Go разработчиков будет где-то между "никогда" и "зачем?". И действительно, в большинстве приложений такая задача никогда не появляется.…
🔐runtime/secret: secret.Do 🔐

Тем временем, абсолютно буднично и рутинно произошло хорошее: пакет runtime/secret получил свою реализацию и будет доступен в Go 1.26 (правда будет скрыт за флагом GOEXPERIMENT=runtimesecret устанавливаемым во время компиляции).

Собственно весь пакет состоит из одной функции: secret.Do, которая принимает на вход функцию с сигнатурой func() и подчищает за ней следующие вещи на выходе из этой функции:
Весь стек который использовался во время работы горутины заполняется нулями. Если при работе стек «рос» (по факту — копировался в новое место) то прямо во время копии старый участок памяти рантайм заполнит нулями.
Все регистры ЦПУ которые «могут содержать секреты» будут заполнены нулями после выхода. Подозреваю, что здесь речь идет о регистрах которые не используются самим рантаймом для служебных действий (верх, низ стека и прочая).
Если в процессе работы переданной функции мы создаем объекты в хипе, то сразу после сборки мусора, их память будет заполнена нулями.
Если внутри передаваемой функции произошла паника, подменяют стектрейс, что-бы скрыть любую информацию о переданной функции.

Для большинства это изменение ничего не меняет.

Но для тех кто работает с криптографией (включая TLS которое мы все используем в HTTPS, HTTP/2 и gRPC без флага insecure) это хорошая новость, которая позволяет усилить защиту приложений и усложнить чтение секретов злонамеренными акторами, даже если скомпрометирована вся железка. Плюс «надежную» очистку секретов часто требуют при сертификации софта в разных регионах нашей планеты.

П.С. Пакет пока доступен только для архитектур amd64 и arm64 (если вы не поняли, что это значит, это хорошо тк вы точно попадаете в доступные архитектуры).
П.П.С. Так-же пока пакет работает только под Linux.
🔥445🤯1
Про go mod tidy | verify | download

Задумывались ли вы о том, как именно Go проверяет целостность ваших скаченных модулей? Как наш тулчейн проверяет, что зависимости которые вы видите в своем проекте, соответствуют зависимостям которые видят другие разработчики?

Я думаю, что нет, ибо работает простой принцип: работает — не трогай. Однако мне, по роду деятельности, пришлось залезть внутрь и прочитать (несколько раз) спеку и посмотреть реализацию. И для того, чтоб структурировать свои изыскания я пишу сей пост.

Все начинается с попытки получить модуль. После первого скачивания модуля (через вызов go mod tidy или go mod download на проекте) в формате zip архива с прокси для модулей, Go делает следующее

Сортирует имена файлов в архиве и пробегает sha256 по каждому файлу. Получившийся набор пар "hash filepath" он прогоняет через sha256 еще раз, кодирует его в base64, добавляет префикс h1: и запоминает результат. В общем виде это выглядит как вызов вот такой команды:


sha256sum $(find . -type f | sort) | sha256sum


Далее он сравнивает получившийся хеш с записью о зависимости которая хранится в go.sum внутри вашего проекта. Если её нет (или нет go.sum, т.е. вы только начали проект) то он идет в GOSUMDB и спрашивает хеш у него. В обоих случаях, после совпадения хешей, zip файл модуля пишется в локальные загрузки ($GOPATH/pkg/mod/cache/download/<url>) и распаковывается в локальный кеш ($GOPATH/pkg/mod/cache/<url>). Если не совпало, ничего не пишем и кидаем ошибку. Побочный вывод — нельзя называть модуль cache или sumdb, о чем ниже.

- Если модуль приватный (выставлены переменные окружения GOPRIVATE и/или GONOSUMDB) или вообще отключена связь с сервером хешсумм через GOSUMDB=off, то проверять хешсумму он будет исключительно с тем, что есть в go.sum внутри проекта. Если там такого модуля нет, то мы доверяем полученным данным и добавляем полученную хешсумму в go.sum.

- Если нет прокси для модулей (например вы берёте файлы напрямую с гита) то Go сам создает zip архив и помещает его в скаченное.

- Подсчет хеша архива модуля недешевая операция в общем смысле, особенно если её постоянно вызывать. Поэтому, кроме zip файла модуля мы рядом храним полученный хеш в файле c суффиксом .ziphash.

Во время сборки бинаря наш компилятор, чтобы не терять время, не проверяет целостность архива и распакованных данных, а только сравнивает, что строчки в go.sum проекта совпадают с тем, что у нас лежит в ziphash файлах.

И тут наступает вопрос: а как проверить, что никто не трогал наш кеш модулей? В дело вступает go mod verify, который пробегаясь по всем модулям из go.mod делает следующее:

Сначала он читает хеш из ziphash файла.
Затем он читает и хеширует распакованные файлы модуля, сравнивая их итоговый хеш с полученным на прошлом этапе. Затем тоже самое повторяется для zip архива модуля. Операция аналогична тому, что происходит при скачивании.
Если не совпало, то verify кидает ошибку на модуль и выходит.

Обратите внимание, что здесь go.sum проекта нигде не участвует. Проверяется целостность именно кеша.

А что если мне подменили ziphash вместе с файлами модуля и zip архивом? На это есть ряд ответов:

Во время скачивания предполагается, что между нами и БД хешсумм установленно защищенное соединение.
Сама БД хешей, это даже не БД в общем смысле, а дерево, где каждый новый элемент зависит от прошлых вставок. Поэтому при попытке поменять запись развалятся записи о всех хешах модулей которые были вставлены после. Кому интересны подробности рекомендую вот эту статью https://research.swtch.com/tlog и погуглить Merkle Tree.
Когда мы скачиваем хеш, мы получаем не отдельное значение, а некое «поддерево» хешей которое необходимо нам для валидации нашего модуля. Это дерево мы помещаем в $GOPATH/pkg/mod/cache/download/sumdb/ (тот самый) и используем для проверки локального модуля. И тут происходит интересное: если удалить ziphash файл то даже без соединения с интернетом, go mod download сможет восстановить хеши из частей большого дерева лежащих в $GOPATH/pkg/mod/cache/download/sumdb/ и записать их обратно в ziphash.
👍174🤯3🔥1
Про go mod tidy | verify | download (Part 2)

Получается, что на практике подделать запись (или случайно закараптить модуль) довольно сложно, тк проверка идет из нескольких мест. Однако потенциальная атака существует:

Атакующий может переписать модуль, его архив и ziphash файлы.
Затем атакующий переписывает хеш внутри go.sum ваших проектов.
Тогда, даже если вы перед сборкой делаете go mod download и go mod verify, то компилятор не заподозрит подмену.

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


find $GOPATH/pkg/mod/cache/ -type f -name "*.ziphash" | xargs -rn1 rm
🔥43👍1🗿1
Forwarded from Thank Go! (Anton Zhiyanov)
Интерактивный тур по Go 1.26

Опубликовал традиционный тур по будущему релизу (на англ). Часть фич мы с вами уже разобрали, а часть еще разберем, но если хотите прочитать все вместе уже сейчас — добро пожаловать.

Вот что вошло:

— new(expr)
— Безопасная проверка ошибок
— Новый «чайный» GC
— Ускоренный cgo и выделение памяти
— SIMD для amd64
— Секретный режим
— Криптография без ридеров
— Профиль для ловли утекающих горутин
— Метрики состояния горутин
— Итераторы в reflect
— Подсматривание в байтовый буфер
— Дескриптор процесса ОС
— Сигнал как причина в контексте
— Сравнение IP-подсетей
— Dialer с контекстом
— Фальшивый example.com
— Оптимизированные fmt.Errorf и io.ReadAll
— Множественные хендлеры в логах
— Артефакты тестов
— Обновленный go fix

Это жесть сколько всего они в релиз запихнули 😅

https://antonz.org/go-1-26
🔥1732
Антон традиционно публикует список нововведений которые приедут к нам с Go 1.26. Самое интересное это new(…expr…), pprof для ловли утечки горутин и обновленный go fix. Рекомендую ознакомится, тем более всё представлено в интерактивном виде.
🔥3
🚀️️️️️️ proposal: spec: direct reference to embedded fields in struct literals 🚀️️️️️️

Embedding структур часто используется как некая «замена наследованию», для того что-бы объединить общий код для нескольких типов внутри одного встраиваемого типа. Это довольно удобно, тк вместе с полями мы получаем еще и методы структуры, при этом обращаться к ним можно как через имя встроенной структуры, так и напрямую. Например


type E struct {
A int
}

type T struct {
E
}

var v T
v.E.A = 100
println(v.A)


Однако «символьная» инициализация подобной структуры всегда требовала полный синтаксис:


v := T{E: E{A: 1}}


Читается сие, на мой взгляд, довольно сложно. Есть и другая проблема: такую методику «выноса» нельзя использовать как рефакторинг, ведь мы ломаем инициализацию у пользователей нашего кода. Этим озаботились и сами разработчики компилятора Go, и предложили разрешить обращаться к полям встроенной структуры, как к родным в момент инициализации. Т.е. В будущем можно будет писать и так:


v := T{A: 1}


Правда есть одно но: а что делать если встроена не структура, а указатель? Кидать панику? Делать «неявную» аллокацию для встроенного указателя? А если таких «встроек» несколько?

Из-за потенциальной (и множественной) сложности выводов, на данный момент, принято решение разрешить новый синтаксис инициализации только для полей-значений. Т.е. код


type F struct {
*E
}

v := F{A: 100}


как и раньше, не скомпилируется. На мой взгляд, это хорошее решение.

Статус issue: likely accept accepted!, а значит есть все шансы увидеть новый синтаксис в Go 1.27.

П.С. Само предложение датируется аж 2015ым. Похоже Go Team основательно взяла курс на разгребание старых проблем.
👍171
😳😳 proposal: spec: generic methods for Go 😳😳

Я не так часто пишу тексты в выходные, но тут случилось исключение, которое я не ожидал увидеть от слова «совсем»: Go Team (а именно Роберт Гризмер, один их трех «столпов» Go) предлагает добавить в язык дженерик методы.

Тут надо сделать отступление: просьбы об этом от сообщества шли давно. Невозможность иметь дженерик методы существенно усложняла часть кода и делала невозможными «поточные» типы. Но сами просьбы разбивались о взаимодействие интерфейсов и дженерик методов у типов: настолько часты были эти просьбы и ответное разъяснение, что в Go FAQ есть специальный пункт про это, который я очень рекомендую прочитать что-бы осознать суть (и глубину) проблемы.

We do not anticipate that Go will ever add generic methods


Так что-же поменялось? Ну во первых «запрос о добавлении дженерик методов в Go» один из самых популярных. 49085 набрало больше 900 положительных emoji, а вопрос «как сделать дженерик методы в Go» остается одним из самых популярных на профильных ресурсах. Во вторых сам Роберт предполагает, что изначальная постановка о дуализме «методы <-> интерфейсы» может быть неверна.

В чем суть нового предложения? По сути предлагается разрешить следующую запись:

type S struct { ... }
func (*S) m[P any](x P) { ... }

type G[P any] struct{ ... }
func (*G[P]) m[Q any](x Q) { ... }


Да-да, теперь можно написать те самые Type[In any].Map[Out any] методы к которым привыкли программисты из С++/Java|C#.

При этом эти методы обладают двумя значимыми ограничениями:

Они не реализуют интерфейс даже при совпадении имени и сигнатуры.

type I interface {
m(int)
}

type H struct{ … }
func (H) m[P any](P) { … }

var h H
var _ I = h // ошибка: сигнатура H.m где m[P any](P) не удовлетворяет m(int) типа I


Пример который более приближен к реальности

type Reader struct{ … }
func (*Reader) Read[E any]([]E) (int, error) { … }


Не удовлетворяет io.Reader даже если в коде есть вызовы (*Reader).Read[byte].

Их нельзя «увидеть» с помощью пакета reflection.
Так как инстанцирование методов происходит во время компиляции, рантайм банально не знает как «увидеть» этот метод динамически. И вызвать его тоже не может.

Сам proposal прописывает довольно много технических подробностей и необходимую работу внутри компилятора (которой, на удивление, относительно немного). Но главное тут другое: основная мотивация это удобство записи: x.a().b().c() писать легче чем c(b(a(x)) и отсутствие дженерик методов существенно усложняет процесс портирования streaming типов из других языков. Насколько хороша эта мотивация покажет время, но тот факт, что автором предложения является один из оригинальных авторов языков, а так-же то, что предложение (на данный момент) позитивно принято сообщество (400+ позитивных emoji) наводит на мысль, что проблема действительно актуальна.

P.S: оригинальный proposal про дженерики содержал такой параграф.

Or, we could decide that parameterized methods do not, in fact, implement interfaces, but then it's much less clear why we need methods at all. If we disregard interfaces, any parameterized method can be implemented as a parameterized function.


Те авторы уже изначально закладывали вариант, что могут и передумать. Ну вот и передумали. 😁️️️️️️
🔥176👍3