Skip to content
Documentation GitHub
Build Errors

Rust Clippy Idioms Catalog

Rust Clippy Idioms Catalog

Problem

cargo clippy reports warnings that, if ignored, accumulate and mask real issues. This project enforces cargo clippy -- -D warnings (warnings as errors). This document catalogs the most impactful Clippy idioms encountered in this codebase.

Idiom 1: too_many_arguments — Use a Parts/Params Struct

Problem

Functions with more than 7 parameters trigger this lint. Common in Clean Architecture where domain entity constructors accumulate fields over time.

Root Cause

Page::from_parts() grew to 8 positional parameters as fields were added (id, title, page_type, template, blocks, frontmatter, created_at, updated_at). Positional parameters are fragile — swapping two DateTime<Utc> arguments compiles but produces wrong behavior.

Solution

Create a named struct that bundles all parameters:

// Before — 8 positional params, error-prone
pub fn from_parts(
id: Uuid, title: String, page_type: PageType,
template: Option<String>, blocks: Vec<Block>,
frontmatter: HashMap<String, serde_json::Value>,
created_at: DateTime<Utc>, updated_at: DateTime<Utc>,
) -> Result<Self, DomainError> { ... }
// After — named fields, self-documenting
pub struct PageParts {
pub id: Uuid,
pub title: String,
pub page_type: PageType,
pub template: Option<String>,
pub blocks: Vec<Block>,
pub frontmatter: HashMap<String, serde_json::Value>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
pub fn from_parts(parts: PageParts) -> Result<Self, DomainError> { ... }

Call sites become self-documenting:

Page::from_parts(PageParts {
id,
title,
page_type: PageType::default(),
template,
blocks,
frontmatter: HashMap::new(),
created_at,
updated_at,
})

Ripple Effect

Changing a domain constructor touches every layer that constructs that entity:

  • Domain tests (2 call sites)
  • Infrastructure repositories (filesystem + SQLite)

This is expected in Clean Architecture — the domain defines the contract and all layers conform.

Idiom 2: ptr_arg — Use Borrowed Slice Types in Parameters

Problem

Function parameters should use the most general borrowed type, not a reference to an owned type.

The Rule

Don’t UseUse InsteadWhy
&String&strAccepts both &String and &str
&Vec<T>&[T]Accepts both &Vec<T> and &[T]
&PathBuf&PathAccepts both &PathBuf and &Path
&Box<T>&TAccepts both via auto-deref

Example

// Before — unnecessarily restrictive
pub fn serialize_path<S>(path: &PathBuf, serializer: S) -> Result<S::Ok, S::Error>
// After — accepts &Path and &PathBuf via Deref
pub fn serialize_path<S>(path: &Path, serializer: S) -> Result<S::Ok, S::Error>

When This Doesn’t Apply

  • When you need to call methods specific to the owned type (rare)
  • In struct fields (use the owned type: PathBuf, String, Vec<T>)
  • In return types (return the owned type)

Idiom 3: dead_code — Proc-Macro False Positives

Problem

Removing a blanket #[allow(dead_code)] from a module exposes warnings on items that ARE used at runtime — but only through proc-macro-generated dispatch code that clippy cannot trace.

Solution

Use targeted #[allow(dead_code)] with explanatory comments on each item. See full writeup: docs/solutions/build-errors/proc-macro-dead-code-false-positives.md

Quick Decision

  1. Referenced only via proc-macro expansion? → Targeted #[allow(dead_code)] + comment naming the macro
  2. Scaffolded but not yet wired? → Targeted #[allow(dead_code)] + TODO comment
  3. Genuinely unused? → Delete it

Prevention

Best Practices

  • Run cargo clippy -- -D warnings in CI — never allow warnings to accumulate
  • When adding fields to a domain entity, check if the constructor should use a Parts struct
  • Default to borrowed slice types (&str, &[T], &Path) for function parameters
  • Use #[derive(Default)] with #[default] attribute on enum variants instead of manual impl Default
  • Never use blanket #[allow(dead_code)] on modules — prefer targeted per-item suppression

Warning Signs

  • Constructor has more than 5 parameters — consider a struct
  • Function takes &String or &PathBuf — almost certainly should be &str or &Path
  • impl Default that just returns a variant — use #[derive(Default)]

References

Was this page helpful?