Rust Style and Best Practices
This page documents paiOS-specific Rust preferences on top of the official Rust style guide and Development Standards. Use it when making tradeoffs the style guide doesn’t mandate.
Stack vs heap
Section titled “Stack vs heap”We prefer stack allocation when it doesn’t hurt maintainability, especially for embedded and performance-sensitive paths.
- Stack: Fixed size at compile time, no allocator, cache-friendly. Use for concrete types and when the full type is known at compile time.
- Heap (
Box,Vec, etc.): Needed for dynamic size, shared ownership, or when you must pick an implementation at runtime. Use when stack isn’t practical.
On resource-constrained or embedded targets, prefer stack and fixed-capacity types (e.g. heapless) where possible; avoid unnecessary heap.
Generics vs Box<dyn Trait>
Section titled “Generics vs Box<dyn Trait>”When the concrete type is known at compile time (e.g. composition root wired with #[cfg(feature = "...")]), prefer generics so implementations can live on the stack:
// Preferred when the composition root knows concrete types at compile timepub struct Orchestrator<V, A, I, Api, P> { vision: V, audio: A, inference: I, api: Api, peripherals: P,}
impl<V, A, I, Api, P> Orchestrator<V, A, I, Api, P>where V: VisionInterface, A: AudioInterface, I: InferenceInterface, Api: APIInterface, P: PeripheralsInterface,{ pub fn new(vision: V, audio: A, inference: I, api: Api, peripherals: P) -> Self { Self { vision, audio, inference, api, peripherals } }}Use Box<dyn Trait> when you must choose the implementation at runtime (e.g. from config or user input), or when threading many generic parameters would make the API unwieldy and the allocation cost is acceptable.
| Situation | Prefer | Reason |
|---|---|---|
| Composition root, one concrete type per build (e.g. feature flags) | Generics (stack) | No heap, one monomorphized type per profile. |
| Runtime selection of implementation | Box<dyn Trait> |
Single type, flexible. |
| Many dependencies, type list explodes | Box<dyn Trait> |
Keep APIs and call sites manageable. |
Error handling
Section titled “Error handling”Library crates use crate-local thiserror enums for typed, documented errors. The binary (pai-engine) uses anyhow for app-level propagation with context.
// Illustrative example: the real implementation may differ.// Library crate: typed error with thiserror#[derive(Debug, thiserror::Error)]pub enum ConfigError { #[error("failed to read {path}: {source}")] Io { path: PathBuf, #[source] source: std::io::Error }, #[error("failed to parse {format} config {path}: {message}")] Parse { path: PathBuf, format: &'static str, message: String },}
// Binary: anyhow for propagation and contextfn load() -> anyhow::Result<Config> { let raw = std::fs::read_to_string("cfg.toml").context("read config")?; toml::from_str(&raw).context("parse config")}Rules enforced via [workspace.lints]:
- No bare
unwrap()orexpect()in library production code (clippy::unwrap_used,clippy::expect_usedare denied). Use?,unwrap_or_else, or add a#[allow]with a reason comment for genuinely infallible paths. - No
panic!ortodo!in production code (clippy::panic,clippy::todoare denied). Test modules use#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]at their top. - No
unsafewithout a#[allow(unsafe_code)]annotation and a safety comment (unsafe_codeis denied workspace-wide).
Formatting and tooling
Section titled “Formatting and tooling”- Run
cargo fmtfrom the repo root (settings inrustfmt.toml:max_width = 100,edition = "2021"). - Aim for zero clippy warnings:
cd engine && cargo clippy --all-targets --all-features -- -D warnings. - The workspace enforces a deny-list via
[workspace.lints.clippy](panic policy, correctness, suspicious); seeengine/Cargo.toml. - See Development Standards for DoD and required checks.
Documentation
Section titled “Documentation”- Public and notable internal items: Use
///or//!doc comments. See Documentation Guide for Starlight sync. - For terminal snippets in rustdoc, prefer
bash frame="none"where possible (see project Cursor rules).
References
Section titled “References”- Rust Style Guide (official)
- Development Standards: Definition of Done, code quality
- Embedded Rust Book: stack,
heapless, no_std when relevant