Generics, Traits, and Runtime Independence
Most protocol logic uses concrete types and generics: packet number spaces, stream directions, crypto sessions, congestion controllers, and read helpers retain exact compile-time structure.
The runtime boundary uses trait objects deliberately:
Arc<dyn Runtime>
Box<dyn AsyncUdpSocket>
Pin<Box<dyn UdpSender>>
Pin<Box<dyn AsyncTimer>>
The endpoint must choose an executor/socket implementation at runtime and store it uniformly. The cost is insignificant beside network I/O, and it prevents runtime generics from infecting every public connection and stream type.
Read and write polling helpers remain generic over closures and output shapes, letting one checked state transition power several ergonomic methods without virtual dispatch.
This is good boundary placement: erase platform scheduling; keep protocol data and application results typed.