S31: Function with too many parameters (no builder or config struct)
What it looks like
Section titled “What it looks like”Eight parameters, five of them &str:
pub fn create_invoice( customer: &str, street: &str, city: &str, postal_code: &str, country: &str, currency: &str, due_days: u32, vat_percent: u32,) -> String { format!( "{customer}, {street}, {postal_code} {city}, {country}: {currency}, due in {due_days} days, VAT {vat_percent}%" )}A call site, with the city and the postal code swapped:
create_invoice("Ada", "Main St 1", "3000", "Bern", "CH", "CHF", 30, 8) // city and postal code swappedWhy AI agents produce it
Section titled “Why AI agents produce it”- Each new requirement adds one parameter. “Also print the VAT rate” becomes
vat_percent: u32at the end of the list. The agent changes the smallest amount of code, and nobody groups the values. - It copies the Java constructor. A Java class with eight fields often has a constructor with eight parameters. The agent writes
newthe same way. - Rust has no named or default arguments. In Python or C#,
create_invoice(city="Bern", ...)makes a long list safer. Rust has no such feature, so the list stays positional.
Why it is a problem
Section titled “Why it is a problem”- Swapped values compile.
"3000"and"Bern"are both&str. The invoice says “Bern 3000” instead of “3000 Bern”, and no test of the types finds it. The testworse_call_site_swaps_two_stringsshows it. - Call sites cannot be read.
"CHF", 30, 8needs the definition to understand. A reviewer must count positions. - The values that belong together are not grouped. Street, city, postal code, and country are an address. Every function that needs an address repeats the four parameters.
- Changing the list breaks every caller, and a new parameter in the middle can shift the meaning of other values of the same type.
The better version
Section titled “The better version”WorseEight positional parameters, five of the same type.
pub fn create_invoice(customer: &str,street: &str,city: &str,postal_code: &str,country: &str,currency: &str,due_days: u32,vat_percent: u32,) -> String {format!("{customer}, {street}, {postal_code} {city}, {country}: {currency}, due in {due_days} days, VAT {vat_percent}%")}BetterThree parameters: the customer, an
Address, andTerms. Each struct groups values that belong together.pub struct Address {pub street: String,pub city: String,pub postal_code: String,pub country: String,}pub struct Terms {pub currency: String,pub due_days: u32,pub vat_percent: u32,}pub fn create_invoice(customer: &str, address: &Address, terms: &Terms) -> String {let Address {street,city,postal_code,country,} = address;format!("{customer}, {street}, {postal_code} {city}, {country}: {}, due in {} days, VAT {}%",terms.currency, terms.due_days, terms.vat_percent)}
At the call site, every value has a name:
let address = Address { street: "Main St 1".into(), city: "Bern".into(), postal_code: "3000".into(), country: "CH".into(),};let terms = Terms { currency: "CHF".into(), due_days: 30, vat_percent: 8,};create_invoice("Ada", &address, &terms)For types with many optional settings, Default with struct update syntax (..Default::default()) or a builder are the other options. The gallery entry Build a request with optional fields compares them.
When it is acceptable
Section titled “When it is acceptable”- A few parameters of different types, where a swap does not compile:
fn move_to(x: f64, y: f64)is clear by convention, andfn send(to: &Email, body: &str, attempts: u32)cannot be mixed up. - Private helpers called from one place, where grouping the values adds a type for nothing.
- Generated code, such as bindings, that mirrors an external API.
How to detect it
Section titled “How to detect it”- Clippy
too_many_arguments(complexity group, on by default) reports functions with more than seven parameters. The threshold istoo-many-arguments-thresholdinclippy.toml. The example has eight. A codebase that sets a lower threshold finds more. - Look for
#[allow(clippy::too_many_arguments)]. An agent may have added it to silence the warning. See S25:#[allow(...)]to silence clippy or compiler warnings. - Look for two or more parameters of the same type next to each other, especially
&str,u64, andbool.
What to tell your agent
Section titled “What to tell your agent”Group the parameters of create_invoice into structs of values that belongtogether: Address { street, city, postal_code, country } andTerms { currency, due_days, vat_percent }. Take them by reference. Usenewtypes or enums where two parameters of the same type can be swapped.Remove any #[allow(clippy::too_many_arguments)]. Update the callers tobuild the structs with named fields, and run the tests.Related: S37: Primitive types where a newtype gives safety (u64 for IDs), S15: bool flags or string constants instead of an enum, and Structs and impl.