Generics, Traits, and API Boundaries
rust-analyzer uses traits differently at each architectural layer.
Parser traits such as TokenSource and TreeSink keep the grammar generic over
token and tree representations. Salsa’s Database trait is passed as &dyn salsa::Database through query-heavy compiler internals, giving generated query
machinery one database boundary without parameterizing every HIR type.
RequestDispatcher::on<R> is generic over R: lsp_types::Request. Associated
Params and Result types make mismatched handlers impossible, while const
generic retry policy records cancellation behavior at the call site.
The public ide API deliberately exposes concrete POD-like domain values and
an Analysis snapshot. HIR provides an object-oriented façade over ID-oriented
internals. Closed variations—events, tasks, syntax kinds, completion kinds—use
enums for exhaustive handling. Heterogeneous worker functions and VFS handles
use trait objects because they must occupy one queue or field.
Generics protect local type relationships; façades protect crate relationships. Both matter more here than saving dynamic-dispatch nanoseconds.