Collections and Data Structures
Rule
Use standard-library collections by default and specialize only for required semantics, deterministic ordering, or known performance needs.
Why
Standard collections are familiar and dependency-free. Specialized structures should express real behavior rather than incidental convenience.
Do
- Choose a collection from the access and ordering behavior the code requires.
- Use sorted collections when sorted iteration is part of the model.
- Sort a
Vecat an output boundary when determinism is needed only there. - Preallocate only when size is already known.
- Use
retain,drain, andstd::mem::takefor clear in-place updates. - Use the entry API instead of repeated lookup and insertion.
- Wrap a collection in a domain type when it carries invariants or behavior.
- Add
indexmap,smallvec, arenas, or domain structures only when their semantics or measured cost matter.
Avoid
- Do not add a collection crate for one convenient call site.
- Do not rely on hash iteration order in tests, logs, serialization, or public output.
- Do not choose a sorted collection when ordering has no value.
- Do not preallocate from guesses or optimize before knowing data shape.
- Do not expose raw collection fields when callers could violate invariants.
Public API Notes
Prefer standard collections or iterators in public signatures unless a specialized collection is part of the contract.
Example
#![allow(unused)]
fn main() {
pub fn users_for_output(users: &HashMap<UserId, User>) -> Vec<&User> {
let mut users: Vec<_> = users.values().collect();
users.sort_by_key(|user| user.id());
users
}
}
Exceptions
- Use
IndexMapwhen insertion order is part of the data model. - Use specialized allocation or layout when profiling shows it matters.
- Use deterministic collections in tests when stable ordering clarifies assertions.