📎 Клонирование, которое возвращает тот же тип В Go 1.26 самоссылающееся ограничение дженериков вида [T Constraint[T]] заработало не только для функций, но и для объявлений обобщённых типов. Один из приёмов, которые это открывает, это клонирование с сохранением конкретного типа. Что это решает Представьте пайплайн, стадии которого форкаются под A/B эксперименты, и каждому форку нужно своё независимое состояние. Стадии имеют разные конкретные типы. Если написать обобщённое копирование, которое возвращает интерфейс Stage , вызывающий код получит абстракцию и не сможет использовать результат как *TransformStage без приведения типа. Приведение прячется где-то в глубине кода, компилятор перестаёт вам помогать, а в рантайме появляется место для паники. До 1.26 выразить «операция над T возвращает T » на уровне объявления обобщённого типа было нельзя. Компилятор ругался на рекурсивный тип. Как это работает Идея в том, что параметр типа T появляется в ограничении на сам T . Тип обязан уметь клонировать именно себя: type Cloneable[T any] interface { Clone() T } // DeepCopy возвращает T, а не Cloneable[T]. func DeepCopy[T Cloneable[T]](v T) T { return v.Clone() } Тот же приём на реальном пайплайне выглядит так: type Stage[T any] interface { Clone() T Execute(ctx context.Context, event Event) (Event, error) Name() string } // ForkStage возвращает T, а не Stage[T]. // Вызывающий получает *TransformStage, без приведения. func ForkStage[T Stage[T]](s T) T { return s.Clone() } На вызове конкретный тип остаётся живым: original := &TransformStage{ /* ... */ } fork := ForkStage(original) // fork имеет тип *TransformStage fork.SomeSpecificMethod() // доступно без приведения Тип пронесён через операцию целиком. Ни одного скрытого .(TransformStage) в коде. Паттерн решает не переиспользование алгоритма, а сохранение идентичности типа. Когда значение порождает копию себя, вы хотите получить обратно тот же конкретный тип, а не супертип, про который компилятор молча забывает. Для форков состояния, снапшотов и любых самоклонирующихся структур это убирает целый класс приведений и связанных с ними рантайм-багов. 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoToProduction
БиБиблиотека Go разработчика
Полезные материалы по всему, что может быть полезно разработчику на Go.
Обратная связь: @proglibrary_feedback_bot
Заявки на разработку сайтов, приложений и ботов: @proglib_oursource_bot
По вопросам рекламы: @proglib_adv_bot и @proglib_adv
CategoryOtherLanguageRUFirst seenJul 06, 2026QualityVerified
Subscribers4.6K
Avg views104K
ERR42.1%
Price-- RUB
Price / subscriber--
Price / view--
Metrics updated: Jul 06, 2026
Recent posts
👣 Многие разработчики приходят в Go с багажом паттернов из Java и C#. В результате простой и понятный код постепенно обрастает слоями абстракций, лишними интерфейсами и десятками DTO, которые усложняют поддержку проекта. 🗓 8 июля в 20:00 МСК приглашаем вас на открытый урок в преддверии старта курса «Go-разработчик. Продвинутый уровень». На занятии разберём, почему привычные подходы из других языков не всегда работают в Go, как выстроить архитектуру через Handler → Service → Repository без циклических зависимостей, где действительно нужны интерфейсы и как избежать избыточных абстракций. ❗️ Вы узнаете, какие ошибки чаще всего встречаются на проверках кода, научитесь проектировать приложения с понятным разделением ответственности и поймёте, как писать код, который останется читаемым и через полгода. ➡️ Регистрируйтесь и познакомьтесь с подходом, который помогает писать на Go проще, надёжнее и профессиональнее: https://clc.to/gqjQAA Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
💻 Фреймворк, где файловая система это карта маршрутов Связка Go и HTMX хорошо стартует, но со временем почти каждый проект обрастает одним и тем же обвесом. Диспетчер маршрутов, стек лейаутов, безопасные URL, проверки на устаревшую генерацию, отпечатки ассетов, команды для отладки. Без общего фреймворка каждое приложение изобретает свою приватную конвенцию роутинга и растаскивает строковые пути по хендлерам, шаблонам, редиректам и тестам. goldr стандартизирует этот слой и при этом оставляет приложение явным. ➡️ Что делает goldr goldr это server-first, HTML-first, HTMX-native фреймворк на Go. Он не вытаскивает центр тяжести из Go. Файловая система становится картой маршрутов, .templ файлы владеют HTML, HTMX остаётся видимым прямо в разметке, хендлеры остаются обычным Go, а сгенерированный код берёт на себя повторяющуюся работу вокруг маршрутов. Вы получаете полный рабочий цикл приложения на Go и HTMX. Это маршрутные страницы, вложенные лейауты, HTMX-фрагменты, экшены на мутации, сгенерированные хелперы для URL, live reload, отпечатанные и встроенные статические ресурсы, команды инспекции маршрутов и визуальный инспектор в браузере, который умеет подсвечивать области отрендеренной страницы. При этом приложение по-прежнему владеет своим net/http сервером, миддлварями, статикой, авторизацией, сессиями, парсингом запросов, валидацией и деплоем. goldr не компилирует CSS, не бандлит JavaScript, не регистрирует хендлеры за вас и не выбирает CDN. ➡️ Как устроены маршруты В goldr файловая система и есть карта маршрутов. Директория маршрута это единица локального поведения: app/routes/ layout.go -> логика лейаута для / и ниже layout.templ -> HTML лейаута route.go -> GET / page.templ -> HTML страницы users/ route.go -> GET /users, GET /users/table, POST /users/create page.templ -> HTML страницы users by_id/ route.go -> GET /users/{id} page.templ -> HTML детальной страницы frag_table.templ -> HTML фрагмента Один route.go объявляет страницу, HTMX-фрагменты и POST-экшены для этой части приложения: var Route = goldr.RouteDef{ Page: page, Fragments: goldr.Fragments{ goldr.FragmentRoute("/table", table), }, Actions: goldr.Actions{ goldr.Action(http.MethodPost, "/create", postCreate), }, } Из файловой системы и этих объявлений goldr генерирует диспатч и типобезопасные хелперы URL. Вместо захардкоженных путей шаблоны ссылаются на сгенерированные хелперы: urls.Users.Path() urls.Users.Table.Path() urls.Users.Create.Path() urls.Users.ByID.Bind(id).Path() Важно, что HTMX остаётся видимым на месте вызова. Сгенерированный путь фрагмента подставляется прямо в атрибут hx-get , а не прячется за проприетарными компонентами: templ UsersView() { <button hx-get={ urls.Users.Table.Path() } hx-target="#users-table" hx-swap="innerHTML" > Refresh users </button> <div id="users-table"></div> } Директория by_id/ маппится в динамический сегмент {id} . Подчёркивания в статических именах превращаются в дефисы в URL, поэтому Go-безопасное имя build_info/ отдаёт путь /build-info . ➡️ Команды разработки goldr dev держит локальный цикл в движении. Он генерирует templ и маршруты, отпечатывает ассеты, перезапускает приложение и перезагружает браузер. goldr generate обновляет обвязку маршрутов, хелперы URL, вывод templ и отпечатки ассетов одной командой. goldr check проверяет, что сгенерированные файлы актуальны, ничего при этом не записывая. Команды routes list , routes explain и routes layouts показывают дерево маршрутов из терминала, а routes refs собирает прямые HTMX-ссылки из .templ файлов. Если у вас растёт Go-приложение с HTMX, которое должно оставаться читаемым, goldr стоит посмотреть. ➡️ Репозиторий Иногда маршруты ве
🔥 Открытое занятие по AgentOps — курс стартовал! Сегодня в 19:00 по МСК пройдет первое занятие нового потока, на которое может прийти каждый. Оцените пользу нашего подхода на ретрансляции урока в VK! 👨💻 Спикер: Андрей Носов Тема: Архитектура управления: state machine для AI-агентов Будем разбираться, как использовать State machine в качестве главного оружия против стохастики (непредсказуемости) LLM. Что в программе: ● State machine: инварианты и терминальные состояния; ● Паттерны маршрутизации: Supervisor, ReAct, Plan-and-Solve; ● Детекция циклов и настройка аварийных выходов; ● Абстракция от модели: как сделать каркас, который переживет смену LLM/провайдера; ● Адаптация графов под ограничения локальных моделей; ● Версионирование графов и миграции стейта. Результат занятия: Вы поймете, как спроектировать надежный каркас агента с жестким контролем исполнения и переходов. 👉 Подписывайтесь на нашу группу ВКонтакте , чтобы не пропустить старт трансляции!
👨💻 Шардируйте локи или ускорение in-memory кеша до 8 раз Конкурентный in-memory кеш в Go часто пишут через один sync.Mutex . Пока нагрузка низкая, всё в порядке. Но под конкурентным доступом единственный лок превращается в узкое место, и добавление ядер не помогает, а иногда даже вредит. Миша Стребков собрал один и тот же кеш string → string шестью способами на чистой стандартной библиотеке и прогнал бенчмарки под чтением, сбалансированной нагрузкой и записью на 1–8 ядрах. Порядок победителей меняется в зависимости от профиля, а одно «очевидное» решение с ростом числа ядер работает медленнее . Шесть вариантов 1. naive это обычная map без блокировок, не потокобезопасна и падает на конкурентной записи. 2. mutex использует один sync.Mutex , прост и корректен, но не масштабируется. 3. rwmutex даёт параллельные чтения и эксклюзивную запись через sync.RWMutex . 4. syncmap это встроенная sync.Map . 5. sharded разбивает данные на 256 частей, у каждой свой мьютекс, ключ выбирает часть по хешу. 6. cow реализует copy-on-write через atomic.Pointer , чтения без блокировок, но каждая запись копирует всю мапу. ➡️ Что показали замеры sharded и cow наращивают пропускную способность с ростом ядер, а mutex остаётся плоским. Более того, у mutex на 8 ядрах пропускная способность падает до 0.66 от одноядерной. Чтения не идут параллельно, а кеш-линия с локом гоняется между ядрами. Вы добавили железо и потеряли производительность. rwmutex упирается в потолок около 2× и перестаёт расти после 4 ядер. Счётчик читателей сам становится точкой конкуренции. На записи он оказывается хуже обычного мьютекса. cow выигрывает на чистом чтении с огромным отрывом, 87 миллионов операций в секунду на 8 ядрах. Но как только появляется запись, он проваливается почти в ноль, потому что каждый Set копирует мапу на миллион записей. sharded единственный дизайн, который держится у вершины во всех профилях сразу. Победитель в пятнадцати строках sharded это просто N независимых мап, каждая под своим локом. Хеш ключа выбирает часть, поэтому операции над разными ключами почти никогда не трогают один и тот же лок. Конкуренция падает примерно в N раз: const shards = 256 // степень двойки, чтобы маскировать вместо modulo type part struct { mu sync.Mutex m map[string]string } type Sharded struct{ parts [shards]*part } func (c *Sharded) at(key string) *part { h := uint64(14695981039346656037) // FNV-1a for i := 0; i < len(key); i++ { h = (h ^ uint64(key[i])) * 1099511628211 } return c.parts[h&(shards-1)] } func (c *Sharded) Get(key string) (string, bool) { p := c.at(key) p.mu.Lock() v, ok := p.m[key] p.mu.Unlock() return v, ok } func (c *Sharded) Set(key, value string) { p := c.at(key) p.mu.Lock() p.m[key] = value p.mu.Unlock() } Одна деталь про явный Unlock . На Go 1.26 замер показал, что defer p.mu.Unlock() здесь стоит около +8% (примерно 1 нс) поверх явного анлока. На горячем пути это заметно, поэтому горячие методы разблокируют лок явно. Почему 256 частей Достаточно, чтобы убить конкуренцию, и не так много, чтобы жечь память. Свип количества частей на 8 ядрах и сбалансированной нагрузке растёт круто до 256, а дальше выходит на плато. Переход от одного лока к 256 даёт скачок в 9 раз (с 4.6 до 43 миллионов операций в секунду). После 256 отдача падает, 1024 добавляют +13%, 4096 всего +18% при кратно большем расходе памяти. Цифры на 8 ядрах, ns/op, меньше лучше, равномерное распределение mix mutex rwmutex syncmap sharded cow read-only 168 53 30 21 11.5 read-heavy 168 259 37 22 12000000 balanced 190 282 57 24 46500000 write-heavy 208 222 73 25 82500000 Восьмизначные значения cow в столбце записи реальные и указаны в наносекундах. 82 500 000 нс это 82 миллисекунды на один Set . Такова цена чтений без блокировок. ➡️ Что использовать