diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e88f90b5..78756d14 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,6 +8,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Version releases are the only exception: just the version number (e.g. `0.21.1`) - Prefer merged imports - Use descriptive generic names (`Size`, `Report`), not single letters +- Use descriptive variable and closure parameter names by default — single letters are only allowed in: conventional names (`n` for count, `f` for formatter), comparison closures (`|a, b|`), trivial single-expression closures, fold accumulators, index variables (`i`/`j`/`k` in short closures or index-based loops only), and test fixtures (identical roles only). Never use single letters in multi-line functions or closures - Prefer `where` clauses for multiple trait bounds - Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated - Custom errors: `#[derive(Debug, Display, Error)]` + `#[non_exhaustive]` diff --git a/AGENTS.md b/AGENTS.md index e88f90b5..78756d14 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Version releases are the only exception: just the version number (e.g. `0.21.1`) - Prefer merged imports - Use descriptive generic names (`Size`, `Report`), not single letters +- Use descriptive variable and closure parameter names by default — single letters are only allowed in: conventional names (`n` for count, `f` for formatter), comparison closures (`|a, b|`), trivial single-expression closures, fold accumulators, index variables (`i`/`j`/`k` in short closures or index-based loops only), and test fixtures (identical roles only). Never use single letters in multi-line functions or closures - Prefer `where` clauses for multiple trait bounds - Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated - Custom errors: `#[derive(Debug, Display, Error)]` + `#[non_exhaustive]` diff --git a/CLAUDE.md b/CLAUDE.md index e88f90b5..78756d14 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,6 +8,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Version releases are the only exception: just the version number (e.g. `0.21.1`) - Prefer merged imports - Use descriptive generic names (`Size`, `Report`), not single letters +- Use descriptive variable and closure parameter names by default — single letters are only allowed in: conventional names (`n` for count, `f` for formatter), comparison closures (`|a, b|`), trivial single-expression closures, fold accumulators, index variables (`i`/`j`/`k` in short closures or index-based loops only), and test fixtures (identical roles only). Never use single letters in multi-line functions or closures - Prefer `where` clauses for multiple trait bounds - Derive order: std traits → comparison traits → `Hash` → derive_more → feature-gated - Custom errors: `#[derive(Debug, Display, Error)]` + `#[non_exhaustive]` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96dc7660..74033a2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,6 +95,98 @@ Use **descriptive names** for type parameters, not single letters: Single-letter generics are acceptable only in very short, self-contained trait impls. +### Variable and Closure Parameter Naming + +Use **descriptive names** for variables and closure parameters by default. Single-letter names are permitted only in the specific cases listed below. + +#### When single-letter names are allowed + +- **Comparison closures:** `|a, b|` in `sort_by`, `cmp`, or similar two-argument comparison callbacks — this is idiomatic Rust. + + ```rust + sort_reflection_by(&mut tree, |a, b| a.name.cmp(&b.name)); + ``` + +- **Conventional single-letter names:** `n` for a natural number (unsigned integer / count), `f` for a `fmt::Formatter`, and similar well-established conventions from math or the Rust standard library. Note: for indices, use `index`, `idx`, or `*_index` (e.g., `row_index`) — not `n`. (For `i`/`j`/`k`, see the dedicated rule below.) + + ```rust + fn with_capacity(n: usize) -> Self { todo!() } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { todo!() } + ``` + +- **Index variables (`i`, `j`, `k`):** These may only be used in two contexts: (1) short closures, and (2) index-based loops/iterations (rare in Rust). In all other cases, use `index`, `idx`, or `*_index`. + + ```rust + // OK — short closure + left_indices.zip(right_indices).map(|(i, j)| matrix[i][j]) + + // OK — index-based loop (rare in Rust) + for i in 0..len { /* ... */ } + + // Bad — use a descriptive name instead + let i = items.iter().position(|item| item.is_active()).unwrap(); + ``` + +- **Trivial single-expression closures:** A closure whose body is a single field access, method call, or wrapper may use a single letter when the type and purpose are obvious from context. + + ```rust + .pipe(|x| vec![x]) + ``` + +- **Fold accumulators:** `acc` for the accumulator and a single letter for the element in trivial folds. + + ```rust + .fold(PathBuf::new(), |acc, x| acc.join(x)) + ``` + +- **Test fixtures:** `let a`, `let b`, `let c` for interchangeable specimens with identical roles in equality or comparison tests (e.g., testing commutativity). Do not use single letters when the variables have distinct roles — use `actual`/`expected` or similar descriptive names instead. + + ```rust + let a = vec![3, 1, 2].into_iter().collect::>(); + let b = vec![2, 3, 1].into_iter().collect::>(); + assert_eq!(a, b); + ``` + +#### When single-letter names are NOT allowed + +- **Multi-line functions and closures:** If a function or closure body spans multiple lines (e.g., contains a `let` binding followed by another expression, or multiple chained operations), use a descriptive name. + + ```rust + // Good + .map(|tree_row| { + let columns = build_columns(tree_row); + format_row(&columns) + }) + + // Bad + .map(|t| { + let columns = build_columns(t); + format_row(&columns) + }) + ``` + +- **`let` bindings in non-test code:** Always use descriptive names. + + ```rust + // Good + let metadata = entry.metadata()?; + // Bad + let m = entry.metadata()?; + ``` + +- **Function and method parameters:** Always use descriptive names, except for conventional single-letter names listed above (`n`, `f`, etc.). + +- **Closures with non-obvious context:** If the type or purpose is not immediately clear from the surrounding method chain, use a descriptive name. + + ```rust + // Good — not obvious what the closure receives + .filter_map(|entry| match entry { _ => todo!() }) + .for_each(|child| child.par_sort_by(compare)) + + // Bad — reader must look up what .filter receives + .filter(|x| x.get_mount_point() == mount_point) + ``` + ### Trait Bounds Prefer `where` clauses over inline bounds when there are multiple constraints: