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

Админ: @lepage_d
Download Telegram
Об обсуждениях.

Вот уже более тринадцати лет нормальный синтаксис неподвижно обсуждается на GitHub.
По воле отцов-основателей выступает Повелитель Простоты и правит миллионами микросервисов благодаря мощи своего неисчислимого бойлерплейта.
Он — гниющий реликт минимализма, в ком незримые муки разработчиков продлеваются загадочными отказами «это не вписывается в философию».
Он — незыблемый столп экосистемы, которому каждый день приносят в жертву тысячи бестолковых предложений о try-catch, идиотские идеи
которых были озвучены уже десятки раз, а все самопротиворечащие доводы высказаны и рассмотрены.
На боли и страданиях гоферов стоит тот самый "Idiomatic Go"


Вы знаете, что такое безумие?

Кто-то скажет, что факт того, что язык получивший свой релиз в 2012-ом году, но не имевший дженериков аж до 2022го — это безумие. На что можно резонно возразить: дескать, нормальный синтаксис — это полдела, основная проблема в экосистеме! Другие могут сказать, что почти все реализации (включая и ту, которую в итоге получили мы) обладают рядом минусов и требуется их хорошо продумать, перед тем как вносить этот инструмент в язык. Третьи скажут, что внесение дженериков в язык привлекает внимание C++ любителей всё усложнять, а сам Go был ценен в первую очередь простотой и понятливостью даже свежему после универа программисту. Короче: оправданий можно придумать массу. На самом деле доподлинно никто не знает, почему внедрение дженериков заняло так много времени, поэтому любые попытки найти рациональное объяснение — не более чем «игра для ума» (по-простому — ещё один способ вентиляции лёгких), которая не несёт в себе никакой практической пользы.

Нет, скажу я вам. Безумие это хождение по кругу годами в поисках идеального синтаксиса который устроит всех. Например, обсуждение синтаксиса лямбд, которое ведётся уже более 8 лет и которое, по итогу, так и не сдвинулось с места. Синтаксиса! Не семантики и не деталей реализации. Ибо главный выбор, который остался перед нами, — это то, «будет ли новый синтаксис использовать =>, или ->, а может, \-> для разделения объявления и тела лямбды?». И если кто-то думает, что я шучу, то ему достаточно глянуть issue, в котором уже больше 900 скрытых комментариев, по которым уже несколько раз делали сводку (вручную, с помощью Gabby, с помощью других LLM, комбинацией из трёх прошлых способов) и которое раз за разом возвращается назад, к своему началу. К выбору идеального синтаксиса. К безумию.

Честно — у меня нет цензурных слов. Если вам когда-нибудь хотелось узнать, что такое "Bikeshedding" (в русском языке нет прямого перевода, но термин расшифровывается как «бессмысленное обсуждение тривиальных вопросов»), то это обсуждение — самый наглядный пример, который у меня есть. Где-то полгода назад, когда, как мне казалось, картинка начала выкристаллизовываться, я хотел написать об этом запись в блоге. И попросить людей поучаствовать, если им есть что высказать или подсказать, ведь это шанс поучаствовать в развитии языка, а создатели действительно слушают мнение людей по этому вопросу! Теперь я понимаю — первый раз за долгое время меня спасло то, что я что-то не сделал. Ибо с тех пор, несмотря на как минимум два резюме по итогам «ещё одной итерации обсуждения», продвижения по-прежнему нет.

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

In the grim darkness of the far future there is only "if err != nil".
12👍4🔥2👀2🗿1
⏲️ testing/synctest — останавливаем время ⏲️

Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из-за гонки "кода со временем". Особенно хорошо это ощущается в тестах которые используют time.Ticker, time.Timer и time.AfterFunc для управление потоком исполнения.

Приведу очень простой пример:

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

for {
select {
case <-time.After(500 * time.Millisecond):
println("do work...")
case <-ctx.Done():
println("Context done:", ctx.Err())
}
}


Для того, что-бы убедиться, что наш код выведет "do work..." 4 раза нам нужно подождать примерно 2 секунды, в течении которых наша программа фактически ничего не делает. Более того, нет никаких гарантий, что наш текст будет выведен именно 4 раза, а не 3 или 2 с учетом нагрузки на планировщик ОС.

Когда такой тест у нас один это нормально. Когда у нас их десятки это проблема. А когда у нас их за сотню то это минуты простаивающего CI, который мог бы делать что-то полезное. И тут у нас есть два решения:

• В пределах тестов кратно уменьшить время "ожидания". Допустим не 500мс а 50. Или вообще 5мс. Проблема такого подхода в том, что исполнение (и работа планировщика) тоже занимает время и тест становится менее надежным. При этом проблема "впустую потраченного времени" никуда не уходит.
• Используем одну из сторонних библиотек для "подделки" времени и замыкаемся на ее семантику (и баги) по всей программе.

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

Хорошая новость, что разработчики языка озаботились это проблемой, и в Go 1.25 пакет synctest стал наконец доступен любому желающему. Публичное API содержит всего две функции:

Первая это synctest.Test. Она запускает замыкание в изолированном пространстве-пузыре где время течёт "иначе". Все горутины созданные внутри этого замыкания разделяют этот "пузырь". Время в них стоит на месте, до тех пор пока все горутины не станут "прочно заблокированы". Термин "прочно заблокированы" может прозвучать странно, но по сути это лишь определенный набор блокировок которые приводят к сдвигу времени в "пузыре». Вот их полный список:

• Блокирующая попытка посылки или получения данных из канала созданного в том же "пузыре".
select с блокирующими операциями над каналами созданным в том же "пузыре".
• Вызов sync.Cond.Wait или time.Sleep.
sync.WaitGroup.Wait, если sync.WaitGroup.Add был вызван из того же "пузыря" (не горутины, это важно).

Однако следующие операции не приводят к "прочной блокировке" горутин:
• Лок мьютексов.
• Блокировка на операциях ввода-вывода (файлы, сеть и прочая).
• Сисколлы.

Вторая функция это synctest.Wait(). Ее единственная роль — дождаться момента когда все горутины в "пузыре" достигли "прочной блокировки" и затем вернуться.

Таком образом как только все горутины "прочно заблокированы" происходит одно из следующих:
• Если Wait был вызван, то он возвращается. Время не сдвигается.
• В противном случае время внутри "пузыря" сдвигается на достаточную величину для запуска в работу хотя-бы одной горутины если функция создававшая "пузырь" не закончила исполнение.
• В противном случае, происходит дедлок и наш вызов Test паникует.

Проще всего понять на живом примере (код не влез в этот пост). Обратите внимание, что время выставлено в часы однако время теста и порядок вывода в лог сохраняется раз за разом. Так мы обеспечиваем не только быстрое исполнение, но и ожидаемый детерминизм. Другой плюс нового пакета, заключается в том, что если горутина создавшая "пузырь" выйдет до того, как закончат работу все её дочерние горутины, то вы получите панику (пример). Таким образом на этапе тестирования еще и обеспечивается контроль очистки ресурсов.

Для более подробной документации рекомендую глянуть документацию. Там-же приведены и другие примеры, сценарии и особенности работы.
👍11🔥3
Go Update
⏲️ testing/synctest — останавливаем время ⏲️ Одними из самых сложных сценариев для тестирования являются сценарии где замешано время. Если использовать реальное время то тесты либо а) начинают занимать слишком много времени либо б) теряют в надежности из…
А теперь по простому:

func TestTime(t *testing.T) {
time.Sleep(365 * 24 * time.Hour)
go func() {
println("inside goroutine")
time.Sleep(time.Second)
}()

println("outside goroutine")
time.Sleep(time.Second)
}


Этот код не закончит исполнение в ближайший год.

func TestTime(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
time.Sleep(365 * 24 * time.Hour)
go func() {
println("inside goroutine")
time.Sleep(time.Second)
}()

synctest.Wait()
println("outside goroutine")
time.Sleep(time.Second)
})
}


А этот код закончит исполнение менее чем за микросекунду и всегда выведет сначала "inside goroutine" а потом "outside goroutine".
17👍4
🛡️ Go 1.25: встроенная защита от CSRF в net/http 🛡️

Тем временем разработчики стандартной библиотеки продолжают упрощать нам жизнь. Практически всё кто связаны с вебразработкой слышали про атаку CSRF. Суть в том, что используя форму (и не только) злонамеренный сайт может заставить ваш браузер выполнить, например, перевод денег из банка на другой счёт, если у банка нет защиты от CSRF, а у вас в этот момент действующая кука от ЛК банка.

Я не буду касаться описания принципов работы "same site" и "same origin", так как про это написано уже масса статей, а коснусь лишь основных механизмов защиты от CSRF:

• Вместе с кукой присылать скрытый <input> в котором присылать дубль токена, который идет в самой куке. Если при запросе то, что в форме и то, что в куке не совпало — запрос отклоняется. Недостаток в том, что токен надо привязывать к пользователю и добавлять в каждую форму.
• Проверять через заголовок Origin что инициатор запроса был сам исходный сайт, а не сторонний. Тоже обладает рядом минусов (может быть null при кривых плагинах), и требует довольно сложной настройки для правильной поддержки cross-origin запросов.
• SameSite куки и "Non-simple requests" которые защищают от вариантов атаки HTTP -> HTTPS но в общем случае бесполезны.

На данный момент для защиты от CSRF в нашей экосистеме есть две либы: csrf и nosurf. Обе работают через дубль токена (т.е. требуют донастройки фронта) и обе почти не поддерживаются разработчиками.

Поэтому Go 1.25 в пакет net/http добавили готовую «прослойку» для защиты от CSRF, построенную на механизме Fetch Metadata который присутствует во всех современных браузерах с 2023го года.

Заголовок с сайта банка при запросе через fetch в таком случае будет выглядеть так:

GET /money/foo.json
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty


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


GET /money/cool.jpg
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image


Сам proposal добавляет лишь один тип CrossOriginProtection, который предназначен для оборачивания ваших обработчиков.

Как это работает:

• Браузер добавляет Sec-Fetch-Site (same-origin / same-site / cross-site / none). Хендлер смотрит это заголовок, и если это не same-origin или none а метод это POST/PUT/PATCH/DELETE то запрос режется. Тоже самое если нет Sec-Fetch-Site: тогда смотрим Origin и сравниваем с Host и опять же, в случае несовпадения, режем POST/PUT/PATCH/DELETE запросы.
• В случае если дополнительно установлен список ориджинов через метод AddTrustedOrigin то полученный запрос сравнивается по заголовку Origin пока не будет найдет нужный, или запрос не будет отклонен.
• Запросы без Sec-Fetch-Site и без Origin пропускаются тк их явно не мог сделать браузер, защищать нечего.
• «Безопасные» методы GET/HEAD/OPTIONS всегда пропускаются (уповают на то, что вы не меняете состояние в этих методах и следуете разумным практикам)

Для более сложных случаев предусмотрены методы AddInsecureBypassPattern и функция SetDenyHandler которая явно разрешает запрос.
👍165
Осталось написать (и разобраться) про новый экспериментальный сборщик мусора и программа минимум будет закрыта 😄
👍176😁2
Go Update
🎂 Тем временем Земля сделала полный оборот вокруг Солнца, и мне исполнилось 32. В этот раз буду предельно краток: я жив, у меня всё хорошо, и этот блог не заброшен. Единственное, чего мне пока очень сильно не хватает, — это времени. Заметок скопилось за три…
🎂 Настал тот день когда мне исполнилось 33!

Да, да тот самый год, в который вас поздравляют используя аналогии к одной из самых ярких личностей в истории человечества 😁️️️️️️.

В (не)далеком детстве, когда мне было лет 7-8, я смотрел на взрослых, которым было за 30 (что казалось мне очень-очень далеко), и она казались кем-то, кто такой «опытный, мудрый, знающий». Думал, что вот: вырасту я, и сам буду такой взрослый-серьезный-мудрый. Машину водить, детей воспитывать. В очках ходить.

И вот теперь мне за 30. Я вожу машину (иногда даже две). Я воспитываю сына. Я хожу в очках (что кстати минус). И вот, что я вам скажу: хрень это все про «вырастишь, сам поймешь!». Фраза пустышка, которую так любили повторять родители и старшие близкие в процессе воспитания, но которая ничего под собой не имеет. Я не чувствую себя опытнее. Или мудрее. Или более знающим. Я ничего так и не «понял». Из года в год я надеюсь, что настанет тот самый магический момент когда я стану наконец «взрослым» (и хотя-бы перестану влезать в бессмысленные споры в чатике которые могут съесть час в лучшем случае). Но этот момент всё никак не настаёт.

Тем более последние годы здорово перевернули жизнь, а рождение сына, два года назад, вообще заставило взглянуть на себя по новому. Но вот сказать, что я теперь я стал весь из себя такой «мудрый-разумный» я не могу. В душе мне по прежнему кажется, что я ментально застрял где-то в возрасте 17-19 лет. Я по прежнему смотрю на своих друзей и вижу в них школьников а не серьезных дядь, коими я видел взрослых будучи мелким. При фразе «пропусти дядю!» я по прежнему оглядываюсь, пытаясь найти того самого «дядю». А вот единственное чего с годами становится объективно меньше это время, и как следствие сил. И то просто из-за количества рутинных дел которые приходится делать день за днём. Та вещь фундаментально непонятна когда тебе 8 и времени хватает на всё и даже больше.

В общем тут уже идет пятый параграф и надо бы начать подводить итоги. И вот мой итог на следующий год: надо нормально спать. Наверное это самая важная мудрость, которую я никак не могу постигнуть. Если вам между 18 и 28-30, то знайте, что «ремонт сбитого режима, за два дня, без регистрации и смс» это не навсегда. Я пишу эти строки сонный, ибо лёг поздно (пункт про время), а в 5 утра меня поднял сын «кики мама не, кики папа мими да!», что в переводе означало что «ему хочется кефир, а мама просыпаться отказалась». Пока просыпался-вставал-наливал-ждал-укладывал сон слетел и пришлось укладывать еще и себя (что гораздо дольше). В общем на сегодня сделал вывод, что на следующий ночной сон пойду вместе с ним. Но вот вечером я вероятно опять найду оправдание в голове, что-бы этого не делать…

Какой уж тут «опытный, мудрый, знающий» если банальный сон привести в порядок не можешь.

П.С. Статья про новый сборщик мусора делается, надеюсь успеть до релиза Go 1.25.
🔥4524👏3
Go Update
🎉 Вышел Go 1.24! 🎉 Этот момент настал! Ключевые нововведения: — Дженерики теперь умеют в псевдонимы (aliases) т.е. теперь можно писать так type MyType[T any] = myType[T,*T] Поздравляю тех, кому приходилось работать с указателями в дженериках, теперь…
🎉 Вышел Go 1.25! 🎉

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

♻️ Бинарей в комплекте теперь меньше. Разработчики компилятора активно использовать команду go tool у себя внутри, что убирает необходимость поставлять заготовки в комплекте. Но как минимум go build и go test бинари все еще на месте.

— Новая директива ignore в go.mod. Подробное описание тут. Если в вкратце: для этого вы раньше использовали каталог testdata и каталоги которые начинаются с точки.

— Когда go команда обновляет go 1.xx.y директиву в go.mod она больше не добавляет toolchain директиву.

go vet теперь ругается на неправильное использование sync.WaitGroup.Add и fmt.Sprintf("%s:%d", host, port) ибо есть net.JoinHostPort.

🍵 Новый экспериментальный сборщик мусора. О нем статья будет позже.

🔀️️️️️️ Новый тип runtime/trace.FlightRecorder позволяет записывать только значимые события, а не всё подряд для tracing’га (под капотом используется циклический буфер, который помнит N последних секунд).

🛠 Компилятор теперь генерирует дебаг-инфу в формате DWARF5. Практический итог: бинари едят меньше места и быстрее комбинируются.

🏎️️️️️️ Новый экспериментальный пакет encoding/json/v2. По хорошему про него тоже надо писать отдельную статью, но если в кратце — он намного быстрее того что было внутри encoding/json. А другая хорошая новость заключается в том, что если вы включили GOEXPERIMENT=jsonv2 то больше ничего менять не надо, так как encoding/json сам подключит новую внутряку.

— Тип os.Root приобрел несколько новых методов.

🏎️️️️️️ Функция reflect.TypeAssert позволяет приводить типы из reflect.Value в конкретный тип, минуя потенциально аллоцирующий вызов reflect.Value.Interface.

— Директива GODEBUG=checkfinalizers=1 позволяет понять, как дела в вашей очереди cleanup’ов и finalizer’ов во время каждого GC.

SetDefaultGOMAXPROCS позволяет сбросить настройки GOMAXPROCS если вдруг переменная прилетела через Env или через прошлый вызов GOMAXPROCS.

— Новый метод sync.WaitGroup.Go - больше нет необходимости тащить errgroup если вам не нужен был возврат ошибок и отмена контекста.

🔥testing.T/B/F теперь имеют метод Output() io.Writer который позволяет правильно редиректить вывод внутри вызова теста.

Читать про релиз вот тут.
🔥2633👍2
А ещё планируем с Колей записать подкаст по поводу релиза 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 процентов моих подписчиков включены уведомления о новом потоке бреда сообщении в канале.
👍35😐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.
🔥435🤯1