TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7372 2.61K
👨‍💻 Типизированные подписки на события в Go без приведений

Снова про [T Constraint[T]]. На этом можно построить событийную шину, где обработчик получает конкретный тип события, а не interface{}.

Событийные системы в Go обычно строят одним из двух способов. Либо типизированные каналы, и тогда нужен отдельный канал на каждый тип события, что быстро разрастается. Либо диспатч через interface{} с приведением типа в каждом обработчике. Второй путь гибче, но каждое приведение это место, где код упадёт в рантайме, если тип не совпал. Компилятор при этом молчит.

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

Событие несёт свой тип полезной нагрузки через самоссылку, а обработчик и шина параметризуются этим же типом:
type Event[T any] interface {
EventType() string
Payload() T
}

type Handler[T Event[T]] func(ctx context.Context, event T) error

type TypedBus[T Event[T]] struct {
mu sync.RWMutex
handlers []Handler[T]
}

func (b *TypedBus[T]) Subscribe(h Handler[T]) {
b.mu.Lock()
b.handlers = append(b.handlers, h)
b.mu.Unlock()
}

func (b *TypedBus[T]) Publish(ctx context.Context, event T) error {
b.mu.RLock()
handlers := b.handlers
b.mu.RUnlock()
for _, h := range handlers {
if err := h(ctx, event); err != nil {
return err
}
}
return nil
}


На стороне вызова обработчик получает конкретный тип, а не пустой интерфейс:
bus := &TypedBus[*UserEvent]{}
bus.Subscribe(func(ctx context.Context, e *UserEvent) error {
log.Println(e.UserID) // реальные поля, без приведения
return nil
})


Добавить новый тип события значит создать новый TypedBus[NewEventType]. Если типы обработчика и шины не сойдутся, компилятор сообщит об этом до выката, текстом вроде *UserEvent does not implement Event[*UserEvent]. Сообщение разбирается не с первого взгляда, но оно появляется на сборке, а не в проде.

Самоссылка даёт третий путь между отдельным каналом на каждый тип и универсальным диспатчем через interface{}. Обработчик работает с настоящими полями события, а несоответствие типов ловится компилятором. Для событийных систем это убирает целую категорию рантайм-багов с приведением типов и делает добавление нового события предсказуемым.

📍 Навигация: Вакансии • Задачи • Собесы

🐸 Библиотека Go-разработчика

#GoToProduction
  • 👍 9
More from @goproglib
  1. Sep 25, 2026💡 Код работает. А data race уже есть В Go можно записать значение в одной горутине, прочи…
  2. Sep 23, 2026💥 TCP/IP: что происходит с данными в сети Когда Go-приложение отправляет данные по сети,…
  3. Sep 22, 2026🔴 Встроенные функции len, make, panic — встроенные функции Go, которые можно использовать…
  4. Sep 21, 2026🤩 Go с нуля: серия базовых шпаргалок Собрали путь от Hello, World! до goroutines, channel…
  5. Sep 20, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — [1 2 3 4 5] / [1 2 10] 🔥 — [1…
  6. Sep 19, 2026🛠 Rune — open-source IDE и terminal multiplexer на Go Rune объединяет IDE, терминал и AI-…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →