diff --git a/languages/rust/architect.md b/languages/rust/architect.md index 57f2f13..77e346d 100644 --- a/languages/rust/architect.md +++ b/languages/rust/architect.md @@ -92,6 +92,7 @@ When opening an ADR for a Rust crate, the template should cover: - **`unsafe` without a soundness comment.** Every `unsafe` block needs a justification. - **Undocumented public items.** A `pub` item with no `///` doc comment ships an unexplained contract. - **`dyn Trait` in a hot-path signature** chosen by default rather than measured. +- **Overengineering.** Reaching for a trait, macro, generic, or `unsafe` before a plain function would do, or optimizing before profiling — the "four horsemen" of bad Rust. Prefer the simplest construct that *still honors the idioms in this guide* — simple, not simplistic. Simplicity is never a license to erase intent: a named enum or newtype still beats a fistful of `bool`/`String` parameters ([`idioms.md`](./idioms.md)). ## Related @@ -101,3 +102,4 @@ When opening an ADR for a Rust crate, the template should cover: - Forest's [`AI_POLICY.md`](https://github.com/ChainSafe/forest/blob/main/AI_POLICY.md) — Filecoin/Forest-specific AI norms; informs Rust review at ChainSafe. - [`../../invariants/invariance-framework.md`](../../invariants/invariance-framework.md) — architectural framework this page defers to. - [Effective Rust](https://effective-rust.com/) (Drysdale) and [The Rust Book](https://doc.rust-lang.org/book/) — the canonical practice references this section builds on; cataloged in [`sources.md`](../../references/sources.md). +- [Idiomatic Rust](https://github.com/mre/idiomatic-rust) (peer-reviewed corpus) and the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) — idiomatic-Rust principles and API conventions. diff --git a/languages/rust/idioms.md b/languages/rust/idioms.md index 8d2758a..ba23f5d 100644 --- a/languages/rust/idioms.md +++ b/languages/rust/idioms.md @@ -44,6 +44,51 @@ A `UserId` and `OrderId` are not interchangeable. Newtypes make the type system For numeric newtypes, `derive_more` or hand-written `From`/`Into`/`Display` are common. +## Enums over booleans + +```rust +fn set_visibility(v: Visibility) // Visibility::Hidden — intent is obvious +fn set_visible(visible: bool) // what does set_visible(true) mean at the call site? +``` + +A `bool` parameter erases intent at the call site, and a pair of them makes illegal combinations representable. An `enum` names the states and gets exhaustiveness checking (mre/idiomatic-rust: *Enums Instead of Booleans*). + +## Accept flexible input types + +```rust +fn greet(name: &str) // &str, not &String +fn join(parts: &[&str]) -> String // &[T], not &Vec +fn read(path: impl AsRef) -> ... // &str / String / &Path / PathBuf all work +``` + +Take the most general borrowed form. Callers pass what they already have without converting, and ownership decisions stay at the boundary. A recurring theme across the idiomatic-Rust corpus. + +> **Caveat — don't use this in a `dyn`-compatible trait.** A generic or `impl Trait` parameter on a *trait method* makes the trait no longer `dyn`-compatible (formerly "object safe"), so `Box` / `&dyn MyTrait` stop compiling. In a trait you intend to use behind `dyn`, take the concrete borrowed type in the trait method and expose the flexible form as a wrapper — an inherent `impl dyn MyTrait`, or a default method bounded `where Self: Sized` (which keeps it out of the vtable): +> +> ```rust +> trait Loader { +> fn load(&self, path: &Path) -> io::Result>; // concrete type → `dyn Loader` works +> } +> +> impl dyn Loader { +> fn load_any(&self, path: impl AsRef) -> io::Result> { +> self.load(path.as_ref()) // ergonomic wrapper, off the vtable +> } +> } +> ``` +> +> Free functions and inherent methods have no such constraint — use the flexible form freely. See the dispatch section in [`architect.md`](./architect.md). + +## Conversion method naming + +Name conversions by cost, per the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/): + +- **`as_`** — cheap, borrow-to-borrow, no allocation (`str::as_bytes`). +- **`to_`** — expensive, borrow-to-owned; it allocates (`str::to_string`). +- **`into_`** — consumes `self`, owned-to-owned (`String::into_bytes`). + +Iterator producers are `iter` / `iter_mut` / `into_iter`; getters take no `get_` prefix (`config.timeout()`, not `config.get_timeout()`). + ## Builder ```rust @@ -182,3 +227,4 @@ pub fn fetch(key: &str) -> Result { ... } - [`developer.md`](./developer.md) — fuller rationale. - [`gotchas.md`](./gotchas.md) — what to avoid. - [Effective Rust](https://effective-rust.com/) · [The Rust Book](https://doc.rust-lang.org/book/) — the upstream practice references. +- [Idiomatic Rust](https://github.com/mre/idiomatic-rust) · [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) — the idiomatic-Rust corpus and API conventions. diff --git a/languages/rust/reviewer.md b/languages/rust/reviewer.md index ccf15d5..0045a9e 100644 --- a/languages/rust/reviewer.md +++ b/languages/rust/reviewer.md @@ -42,6 +42,9 @@ Per [reviewer-severity tier table](../../operating-model/gates-and-escalation.md - **Public items are documented.** New `pub` items carry `///` docs (what it does, returns, errors, panics); examples are doc-tested (Effective Rust Item 27). - **Dispatch is justified.** Generics / `impl Trait` on hot paths; `dyn Trait` only where heterogeneity or binary size earns the indirection. - **Breaking changes are intentional.** Removing or renaming a `pub` item, or adding a constructible field, is a SemVer break — flag it. API-visible dependency types are re-exported. +- **Flexible input types.** Public functions take `&str` / `&[T]` / `impl AsRef` over `&String` / `&Vec` / concrete owned types where reasonable. +- **Conversion and getter naming** follow the Rust API Guidelines: `as_` / `to_` / `into_` by cost, `iter` / `iter_mut` / `into_iter`, no `get_` prefix. +- **Enums over boolean parameters** where a `bool` argument erases intent at the call site. ### Concurrency primitives @@ -96,3 +99,4 @@ Per [reviewer-severity tier table](../../operating-model/gates-and-escalation.md - [`../../invariants/agent-era-invariants.md`](../../invariants/agent-era-invariants.md#7-no-bypass-of-reviewer-skill-hard-fail-findings) — HARD FAIL semantics; `unsafe` promotes here. - Forest's [`AI_POLICY.md`](https://github.com/ChainSafe/forest/blob/main/AI_POLICY.md) — the Filecoin-specific extension. - [Effective Rust](https://effective-rust.com/) · [The Rust Book](https://doc.rust-lang.org/book/) — canonical practice references (see [`sources.md`](../../references/sources.md)). +- [Idiomatic Rust](https://github.com/mre/idiomatic-rust) · [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) — idiomatic-Rust corpus and API conventions. diff --git a/references/sources.md b/references/sources.md index aad46c9..3d164ba 100644 --- a/references/sources.md +++ b/references/sources.md @@ -90,6 +90,14 @@ Entries are added when the handbook starts deferring to a new source. Removal is - **Used by.** [`../languages/rust/architect.md`](../languages/rust/architect.md), [`../languages/rust/developer.md`](../languages/rust/developer.md), [`../languages/rust/reviewer.md`](../languages/rust/reviewer.md), [`../languages/rust/idioms.md`](../languages/rust/idioms.md), [`../languages/rust/gotchas.md`](../languages/rust/gotchas.md). ChainSafe's canonical Rust codebase is [Forest](https://github.com/ChainSafe/forest) (Filecoin). - **Coordination.** External upstream, treated as canonical for idiomatic Rust. **Effective Rust is CC-BY-NC-ND** — the handbook references and credits specific items by number and link; it does not reproduce or adapt the text. Re-anchor item links if the upstream reorganizes; the weekly link-check catches rot. +### Idiomatic Rust corpus + Rust API Guidelines + +- **Source.** [mre/idiomatic-rust](https://github.com/mre/idiomatic-rust) — Matthias Endler's peer-reviewed collection of articles, talks, and repos that teach concise, idiomatic Rust (released **CC0** / public domain; sortable index at [corrode.dev/idiomatic-rust](https://corrode.dev/idiomatic-rust/)). The highest-leverage standards it points to are the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) (the official idiomatic-API checklist), [Canonical's Rust Best Practices](https://canonical.github.io/rust-best-practices/), and [Elements of Rust](https://github.com/ferrous-systems/elements-of-rust). +- **Maintainer.** External — Matthias Endler (corpus) and the Rust library team (API Guidelines). Internal coordination point: the Rust CODEOWNER ([@LesnyRumcajs](https://github.com/LesnyRumcajs) / [@hanabi1224](https://github.com/hanabi1224)). +- **Covers.** Idiomatic, ergonomic Rust: flexible input types (`&str` / `&[T]` / `impl AsRef`), conversion and getter naming (`as_` / `to_` / `into_`, `iter` / `iter_mut` / `into_iter`, no `get_` prefix), enums over booleans to express intent, the common conversion traits (`From` / `Into` / `TryFrom` / `AsRef` / `Cow`), and simplicity over overengineering. Complements Effective Rust + The Rust Book; the handbook applies the principles rather than copying the corpus. +- **Used by.** [`../languages/rust/architect.md`](../languages/rust/architect.md), [`../languages/rust/developer.md`](../languages/rust/developer.md), [`../languages/rust/reviewer.md`](../languages/rust/reviewer.md), [`../languages/rust/idioms.md`](../languages/rust/idioms.md), [`../languages/rust/gotchas.md`](../languages/rust/gotchas.md). +- **Coordination.** External upstream. The corpus is CC0 (no attribution required; credited as a courtesy), and the Rust API Guidelines are the stable, authoritative reference for naming and API conventions. Re-anchor links if the upstream reorganizes; the weekly link-check catches rot. + ### Effective Python (idiomatic Python) - **Source.** [Effective Python](https://effectivepython.com/) by Brett Slatkin — *125 Specific Ways to Write Better Python* (3rd ed., Pearson Addison-Wesley; covers the language through Python 3.13). The book is copyrighted and not free online; the companion code is open at [bslatkin/effectivepython](https://github.com/bslatkin/effectivepython). The handbook references items by number and title, and does not reproduce the text. @@ -103,7 +111,7 @@ Entries are added when the handbook starts deferring to a new source. Removal is Surfacing known candidates rather than fabricating coverage: - **Other internal artifacts to canonicalize.** When team members surface practices that exist in product repos or docs and could become canonical sources, they get added here with the original author credited (see [`./attribution.md`](./attribution.md)). -- **Language-specific upstream references.** Per-language reviewer pages may deep-link into language-community standards (e.g., the Rust API guidelines). Those entries are added here when a page starts deferring to one. +- **Language-specific upstream references.** Per-language reviewer pages may deep-link into language-community standards (e.g., Go's *Effective Go*, the TypeScript handbook). Those entries are added here when a page starts deferring to one. ## Related