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

Админ: @lepage_d
Download Telegram
Новый материал по обновлениям в стандартной библиотеке (и не только) Go уже в работе.

А пока я его пишу, вот вам небольшая викторина навеянная недавним багом который я искал в проде.


init[1055]: segfault at 40 ip 00000000008ef4f8 sp 000000c000e23500 error 4 in init[400000+245f000] likely on CPU 0 (core 0, socket 0)


Что выведет код:


type MyString string

func (s *MyString) String() string { return "world! " + string(*s) }

func main() {
var str *MyString

fmt.Println("Hello", str)
}
👍5
Forwarded from Dmitry M
🔥13🤯8👍31
Что-бы дать правильный ответ нужно понимать три вещи:

1. Как работает вызов по nil указателю.
2. Как работает семейство fmt.*Print* функций.
3. Какие паники можно ловить через recover.

Во первых вызов по nil указателю это вполне корректный код в отличии от, например, Java или C++. До тех пор пока вы не обращаетесь к данным внутри переменной типа, никаких ограничений язык не накладывает. Этим пользуются например в grpc генераторах для создания типов с цепочками методов — даже если переменная nil она все равно может быть рабочей. Единственное но: это работает только с конкретными типами. Для интерфейсов нужно что-бы переменная-интерфейс знала о том какой у нее тип внутри. Да-да тот самый typed nil про который любят спрашивать на собесах иногда имеет реальную применимость.

Для второго идем читаем документацию https://pkg.go.dev/fmt#hdr-Format_errors где видим:

If an Error or String method triggers a panic when called by a print routine, the fmt package reformats the error message from the panic, decorating it with an indication that it came through the fmt package. For example, if a String method calls panic("bad"), the resulting formatted message will look like

%!s(PANIC=bad)

The %!s just shows the print verb in use when the failure occurred. If the panic is caused by a nil receiver to an Error or String method, however, the output is the undecorated string, "<nil>".


Т.е. в случае паники fmt.Println постарается перехватить ее и вывести, что было внутри паники. Исключением являются попытки разыменования по нулевому указателю, вывод паники которой даст <nil>. Почитать исходники можно тут: https://go.googlesource.com/go/+/refs/tags/go1.22.2/src/fmt/print.go#587

И наконец третье - перехватить через recover можно любую панику кроме паники о одновременном чтения и записи (или одновременной записью из разных горутин) в тип map. Вызов runtime.Goexit() перехватить тоже нельзя, т.к. он не является паникой.
👍1576🔥2👏1🤯1
А теперь повысим сложность для тех кому интересно:

Ядро очень чутко относится к процессу с PID 1. Оно и понятно: нет процесса — нет юзерспейса ОС. Segfault который вы видели выше, это как-раз и есть эта самая паника с точки зрения ядра. Которое ее видит и пишет в логи. Отсюда получаем неприятный момент — паника как-бы есть, но видим мы ее только потому-что наш Go процесс служит init процессом. Другая возможность увидеть эту панику это подключится к процессу через дебаггер, но с init процессом это довольно проблематично. А вот тесты в таком сценарии не особо помогают.

Я это к чему: было несколько предложений в трекере про функцию для установки глобального перехватчика на паники. Но Go Core Team с упорством каждый раз отклоняет их с фразой «не нужно, есть recover()». Вроде и да, но вот в таких случаях нас спасает только вывод go tool objdump -S и знание ассемблера. И как улучшить ситуацию я, честно говоря, не знаю.
20😁2👍1
Тем временем Shieldy обезумел и не пускал никого в обсуждения. Заменен другим ботом, посмотрим как новый работник справится.

Плз, кому не лень, проверьте, что у вас получается присоединиться к дискуссии и написать сообщение.
😁10👍2
🛠 runtime/debug: add SetCrashOutput

По следам прошлого бага мне тут подсказали, что в Go 1.23 нам дадут глобальную функцию для обработки паник. Однако есть два но:

1. Функция работает только с теми паниками, которые никто не перехватил используя recover().
2. Функция позволяет выбрать только куда выводить сообщение. А если точнее, в какой файловый дескриптор направить вывод.

Т.е. общая идея данной функции состоит в том, что-бы разработчик мог выбрать куда конкретно он будет писать фатальные паники при схлопывании приложения. В документации есть удобный и показательный пример. А кому интересна внутрянка, то все самое важное происходит вот тут. Число изменений минимальное - Go рантайм уже использует файловый дескриптор когда пишет текст паники, поэтому все, что мы (условно) делаем это атомарно подменяем этот самый дескриптор в функции SetCrashOutput.

Однако для случая выше это бы никак не помогло, т.к. паника уже перехвачена с помощью recover() во внутрянке пакета fmt. Моя идея состояла скорее в глобальном хуке, который позволяет вклиниться в любую панику до ее обработки. Но именно для анализа, а не для остановки или восстановления т.к. для этого уже есть другие механизмы.

П.С. Спасибо Алексею Палажченко за наводку.
👍15🔥51
Go Update
Персональная запись: сегодня мне исполнился 31 год. Сегодня, как и в каждом году, в этот день и месяц, число прожитых мной лет снова сделало инкремент на единицу. Это будет небольшая персональная заметка о том, что было и возможно будет. Идея вести такой…
🎂 Тем временем Земля сделала полный оборот вокруг Солнца, и мне исполнилось 32.

В этот раз буду предельно краток: я жив, у меня всё хорошо, и этот блог не заброшен. Единственное, чего мне пока очень сильно не хватает, — это времени. Заметок скопилось за три десятка, но оформить их во что-то целостное у меня пока никак не получается. Очень надеюсь, что к релизу Go 1.23, который будет в ближайшие одну-две недели, у меня наконец появится время для оформления небольшого обзора, ибо, кроме итераторов, там есть и другие интересные и приятные вещи.

На этом у меня всё. Увидимся ещё через год в ближайшее время.
👍539👏3🔥1💩1
🎉 Вышел Go 1.23! 🎉

Ключевые нововведения:
Итераторы — больше вот тут. TLDR: теперь можно делать range по таким функциям:


func(func() bool)
func(func(K) bool)
func(func(K, V) bool)


• Opt-in телеметрия — опциональный сбор метрик и периодическая отправка их. Детальный док вот тут. TLDR: эта вещь позволяет понять как часто и каким командами пользуются Go разработчики, какие флаги они применяют и как часто люди сталкиваются с ошибками компилятора. Статистика обезличенная, а сам формат полностью в открытом доступе. Важно! Сбор по умолчанию включен, но никуда не отсылает метрики. Включить отправку можно с помощью команды go telemetry on. Выключить полностью можно с помощью команды go telemetry off.
• Директива godebug в go.mod файлах.
• Больше нельзя обратится к неэкспортируемым именам в стандартной библиотеке с помощью //go:linkname (за исключением ряда функций).
time.Timer и time.Ticker больше не нужно останавливать для того, что-бы сборщик мусора смог их собрать. Во вторых, канал который ассоциирован с этими таймерами теперь небуферизированный и гарантирует, что в канале нет данных после остановки. Другими словами, после остановки time.Timer и time.Ticker больше не требуется пытаться вычитать данные из канала, на случай если таймер уже истек и послал данные в канал. Включается только если go.mod содержит go 1.23.0 и выше. Заметку про это обновление можно прочитать вот тут.
• Пакет unique. Про него тоже писал раньше.
Много функции для итераторов.

Полное описание релиза вот тут.
🔥529👍4
✍️ Официальный мануал о том, как создавать и работать с итераторами.

Go Team опубликовали достаточно подробную инструкцию о том, как работать с новыми механизмом range-over-func (range по функциям, итераторы). Запись в блоге достаточно короткая, что ее можно прочитать за бизнес-ланчем. А с ее переводом легко справится тот-же Google Translate, если вы не уверены в своем английском. А для ответа на часто задаваемые вопросы еще есть эта запись.

TL;DR: теперь можно делать так:


sortedKeys := slices.Sorted(maps.Keys(myMap))
🔥22👍81
Всем привет! Небольшое объявление: 3го октября в 18:30 я буду выступать на митапе Витеха. Подключайтесь онлайн или приходите лично!

А кроме меня там будут:
🔦 Кирилл Кузин
Бросил работу инженера-конструктора, чтобы разрабатывать на Go. Сейчас работает над сервисами и PaaS в Ви.Tech

🔨 Эдгар Сипки
Эксперт в Go с 2018 года. Основатель open-source инструмента EasyP, член ПК GolangConf. Ozon Банк.

🛠 Даниил Подольский
Глава программного комитета GolangConf, активист Go Spb и просто хороший человек. YADRO.

🔩 Алексей Мясников
Руководитель команды в Яндекс — на проекте YDB. В IT с 2006 года. Писал код на более чем 20 языках программирования: больше всего на C++, Java, Go, TypeScript.

🗓 3 октября, 18:30 мск, Четверг
📍Офлайн | 💻 Онлайн

🔧ОГО! Ви.Tech meetup #1

Приходите на первый митап по Go от IT-команды ВсеИнструменты.ру! Обсудим интеграцию gRPC с frontend, замену слайсов итераторами и оптимизацию Go через компилятор. Завершим дискуссией с экспертами и нетворкингом

🔗 Регистрируйтесь по ссылке
👍12🔥41
crypto/rand: crash process on error reading randomness

Интересное улучшение/изменение которое грядет в Go 1.24: функция Read и переменная Reader в пакете crypto/rand больше никогда не будут возвращать ошибку, несмотря на то, что сигнатура останется прежней. Связано это с тем, что на всех современных операционных системах, функции для получения криптографически случайных данных никогда не возвращают ошибку.

В самой функции, при этом, вставлена паника для проверки того, что контракт исполняется.
👍17
net/http: delete inappropriate headers in func Error

Из недавних мелких изменений: начиная с Go 1.23 (и выставленной директиве go 1.23.0 в go.mod) функции ServeContent, ServeFile, and ServeFileFS в случае ошибки удаляют заголовки Cache-Control Content-Encoding, Etag, и Last-Modified. Это стоит учитывать, если у вас есть матрешка вокруг интерфейса ResponseWriter которая, например, сжимает ответ и добавляет заголовок Content-Encoding: gzip.

Старое поведение можно восстановить с помощью директивы GODEBUG=httpservecontentkeepheaders=1.

Изначально планировалось сделать тоже самое для функции http.Error. В последствии от этого отказались, так как это изменение ломало слишком много пользовательского кода.
👍73
🔥 weak: new package providing weak pointers 🔥

Соскучились по интересным изменениям? Их есть у меня!

Начиная с Go 1.24 у нас появится пакет который даст там т.н. "слабые указатели". Суть этих указателей заключается в том, что сборщик мусора вправе в любой момент времени собрать данные на которые они указывают. Т.е. сделать их указатели невалидными.

Простой пример кода:

weakPtr := weak.Make(&SomeStruct{someData})
runtime.GC()
println(weakPtr.Pointer() == nil) // может быть true, а может и false


На самом деле эти указатели появились еще с приходом пакета unique о котором я писал ранее. Но затащить два пакета в рамках одного proposal'а нельзя, поэтому пришлось ждать Go 1.24. Go Core Team (и в особенности Расс Кокс) очень долго сопротивлялись добавлению этого вида указателей в язык, т.н. они ломают абстракцию со сборщиком мусора и имеют очень неочевидные механики взаимодействия с finalizers. Однако после добавление пакета unique отрицать необходимость (хоть и редкую) такой вещи стало невозможно, поэтому было принято решение дать ее использовать всем.

Основной плюс этого пакета в том, что разработчикам кешей стало намного легче собирать мусор. Даже человек, который слабо помнит алгоритмы, теперь может реализовать LRU кеши элементарно на мапах. Как именно? А вот это я покажу в сообщении о следующем за этим изменении.
🔥33👍321
🧹 runtime: add AddCleanup - типобезопасная альтернатива SetFinalizer

Сейчас для отслеживания настоящего момента удаления объекта из памяти у нас есть одна функция: runtime.SetFinalizer. Она говорит рантайму, что нас (через коллбэк) нужно уведомить о том, что объект был убран из памяти сборщиком мусора. Чаще всего это нужно, чтобы мы не забыли закрыть внешние дескрипторы (файлы/сеть/прочее), ассоциированные с объектом.

Однако у этой функции есть и ряд минусов:
Она не является типобезопасной. Вот вам простой пример кода, который развалится только во время выполнения.
Она допускает "воскрешение мёртвых объектов" (ситуации, когда через финализатор можно вернуть данные по удаляемому указателю обратно к жизни - пример кода). Эту штуку кстати ребята из TailScale использовали в своей либе go4.org/intern. У них еще крутая статья есть.
Нельзя поставить несколько финализаторов на объект. Это здорово мешает, когда есть дополнительные вещи которые мы хотим выполнить при «смерти» объекта, а автор вызываемого кода уже повесил свой финализатор. Обходные пути есть, но у всех них есть свои тонкости.

В качестве решения предложена функция AddCleanup вот с такой сигнатурой:


func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup

type Cleanup struct { ... }

// Stop cancels the cleanup call.
func (c Cleanup) Stop() { ... }


Суть в том, что мы вешаем коллбэк на указатель (ptr), но в сам коллбэк приходит объект (arg) который мы ассоциировали с указателем.

Что это нам дает?
Больше нельзя указать неправильную сигнатуру для коллбэка.
Идет разделение между тем, что мы отслеживаем и тем, что мы получаем в коллбэк при «смерти» отслеживаемого. Более того, сама функция пытается уберечь нас от ситуаций типо arg == ptr (правда, только в рантайме).
Сначала выполняется финализатор (если он есть), а затем выполняется один или более коллбэк который мы повесили с помощью нового механизма.

А ждет нас это счастье в Go 1.24, ведь само изменение уже в мастере.

LRU Cache Example

На самом деле я немного покривил душой, когда говорил про LRU кеш: получаемый кеш имеет часть его свойств, но нельзя точно указать число объектов которые будут живы в памяти и время их жизни. Те из вас кто работал с другими языками скорее всего узнают WeakMap в самом примере.

Главное:
Создаем словарь/мапу ключей к слабым указателям.
Удаляем ключ как сработает AddCleanup.

На этом всё - у вас есть минимальный пример самоочищающегося кеша.
🔥164🤯3
🐧 Go 1.24 будет требовать ядро Linux 3.2 и выше.

Небольшая, но важная, заметка. В 2021 году (Go 1.18) минимальным ядром на Linux стала версия 2.6.32. Спустя почти четыре года, нас снова ждет изменение требования: теперь в Go 1.24 минимальную версию ядра поднимут до 3.2. Учтите это при обновлении, если у вас очень старые дистрибутивы на серверах (или у вас зоопарк), а разрабатываете вы на самом новом и крутом.

П.С. Да, я в курсе, что актуальная версия ядра вообще 6.x.y 🙂️️️️️️
👍252
Минутка юмора.

Можете отправить это человеку который продолжает вас убежать в том, что ChatGPT скоро заменит всех разработчиков инженеров людей.
😁44🗿7🤡2😐1
Про нововведения в Go 1.24

Долго собирался написать серию (хотя бы один!) постов про вещи которые к нам подъедут в Go 1.24, но так никак и не смог собраться. А потом обнаружил, что один наш коллега уже все это сделал в подробном и, главное, интерактивным виде. Правда есть нюанс — сам пост на английском.

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

П.С. Ну а я постараюсь в ближайшее время запилить обзор предложение от Яна Ланса Тейлора о новом синтаксисе обработки ошибок и как оно разделило сообщество почти поровну.
👍121
0️⃣ Omitzero или убираем пустые значения при серилизации в json

Я уверен, что большинство в курсе о том, что пакет json поддерживает структурный тег omitempty. На данный момент это единственный прямолинейный способ указать, что поле не следует выводить в json если оно содержит нулевое значение. Однако у этого тега есть два минуса:

1. Он плохо работает со структурами.
2. Он неумеет в переопределение принципа по которому определяется сам факт нулевого значения.

Оба пункта довольно долго портили кровь разработчикам сложных вложенных API и заставляли их пользоваться указателями, там, где можно было обойтись без них. И вот Go Team приняла волевое решение и реализовала новый тег. Встречаем omitzero. Принцип работы прост до невозможности:

1. Если поле это структура и она пустая, то маршалить ее он не будет.
2. Если у типа поля определен метод IsZero и он вернул true то и его маршалить не будут.

КМК именно это можно было привезти еще в версии Go 1.10, т.к. фиче не нужны новые языковые фишки, а необходимость ее была видна еще с Go 1.1. Чтож, лучше поздно чем никогда.
👍358
🎉 Вышел Go 1.24! 🎉

Этот момент настал!

Ключевые нововведения:
— Дженерики теперь умеют в псевдонимы (aliases) т.е. теперь можно писать так


type MyType[T any] = myType[T,*T]


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

— Теперь можно отслеживать версии и использовать утилиты прямо с помощью команды go tool. Краткий смысл сего: оно помещает все зависимости добавляемой тулзы в require блок, а саму утилиту в блок tool. Учтите, что если вы не указываете отдельный go.mod файл с помощью флага -modfile (его принимают большинство команд) то зависимости внешней тулзы перемешаются с вашими. Однако есть и хорошая новость — из-за умного механизма «вычистки» MVS если кто то импортирует ваш модуль, то зависимости которые нужны для утилит к нему не попадут.

go test теперь гоняет go vet с небольшим числом анализаторов перед запуском тестов для поиска распространенных ошибок (в том числе ошибок в оформлении тестов). go vet тоже подтянули, теперь, например, ругается на printf функции где аргумент только строка.

— Бинари запущенные с помощью go run и go tool теперь кешируются внутри внутри билд кэша. Никаких больше пересборок на каждом go run.

go build и go install научили в json вывод.

— С помощью новой переменной окружения GOAUTH можно определить принцип для авторизации на приватных серверах модулей. Подробности с помощью go help goauth

go build теперь вставляет в бинарь информацию о версии (наконец-то можно перестать делать это вручную!) с использованием тега и/или хеша коммита вашего гита (или mercurial и прочая).

— Новая реализация map на основе Swiss Tables. Бигтехи ваши вопросы на собесах больше неактуальны 🤡.

— Инлайнинг стал мощнее. Теперь, с большей вероятностью, тело функции которую вы передаете как аргумент будет вставлено в место ее вызова. Делали в основном для итераторов которые уступали по скорости циклам. Вместе с прошлым изменением и прочими изменениями в рантайме дало совокупный буст в 3-5% по производительности.

— Различные улучшения CGO с помощью новых директив.

— Новый способ гонять бенчи for b.Loop() { ... }. Основной плюс (кроме того, что меньше писать) в том, что гонялке бенчмарков больше не нужно вызывать вашу функцию несколько раз, ибо сама найдет нужные параметры во время цикла. А значит дорогие блоки инициализации и удаления по итогу бенча стали дешевле.

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

— Пакет со слабыми указателями стал доступен всем.

— Подъехала замена финалайзерам. С типобезопастностью и сигнатурой которая намекает как делать правильно (и длинной докой объясняющей все сценарии). А еще их можно повесить сколько угодно в отличии от финалайзеров. Почему это важно я писал ранее.

— Пакет testing/synctest который в будущем позволит нам полностью дропнуть все моки времени. Пока обещают изменения API поэтому скрыто за флагом GOEXPERIMENT=synctest

— Появился OpenRoot. TLDR: открыв каталог таким образом из него нельзя убежать используя симлинки и прочая. Штука интересная для тех у кого пути до файлов генерируются извне.

— Куча прочих улучшений, включая новый функции для итераторов в пакеты bytes и strings.

— У тестов и бенчей появился свой контекст! Можно будет дропнуть кучу кода отвечающего за создание контекстов в тестах.

Полное описание (жирного) релиза вот тут.
🔥41👍186