package main import ( "context" "fmt" "time" ) type Mailbox[T0, T1 any] struct { ctx context.Context inbox chan T0 outbox chan T1 } // Functions for external use func (m Mailbox[T0, T1]) Send(v T0) { m.inbox <- v } func (m Mailbox[T0, T1]) Recv() T1 { return <-m.outbox } // Functions for mailbox owner func (m Mailbox[T0, T1]) Read() T0 { return <-m.inbox } func (m Mailbox[T0, T1]) Write(v T1) { m.outbox <- v } // Mostly here for example; you may not want buffered channels func New[T0, T1 any](ctx context.Context, input T0, output T1) mailbox[T0, T1] { var m mailbox[T0, T1] m.ctx = ctx m.inbox = make(chan T0, 32) m.outbox = make(chan T1, 32) return m } func Echo() Mailbox[string, struct{}] { m := New(nil, "", struct{}{}) go func() { for { v := m.Read() m.Write(v) } }() return m } func main() { echo := Echo() echo.Send("Hello") fmt.Println(echo.Recv()) }