rust-lang / rust-lang/rust-clippy

New lint: `interchangeable_params` flag functions where 2+ params share the same type

Open
#16,735 4 comments 14 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

Flags function signatures where two or more parameters share the same type, making them silently swappable at the call site without any compiler warning.

Advantage
  • Catches bugs the type system completely ignores
  • Forces better API design at the signature level
  • The exceptions are narrow enough that what's left flagged is genuinely ambiguous
  • Pure static analysis, no runtime cost, no overhead
Drawbacks

No response

Example
// bad — three IDs and four money values, all swappable within their type
fn process_order_payout(
    shop_id: String,
    customer_id: String,
    order_id: String,
    amount: i64,
    platform_fee: i64,
    tx_fee: i64,
    net_amount: i64,
) {}

// compiles, pays the wrong entity the wrong amount
process_order_payout(customer_id, shop_id, order_id, net_amount, tx_fee, platform_fee, amount);

Could be written as:

// fix — newtypes make each parameter distinct
struct ShopId(String);
struct CustomerId(String);
struct OrderId(String);
struct Amount(i64);
struct PlatformFee(i64);
struct TxFee(i64);
struct NetAmount(i64);

fn process_order_payout(
    shop_id: ShopId,
    customer_id: CustomerId,
    order_id: OrderId,
    amount: Amount,
    platform_fee: PlatformFee,
    tx_fee: TxFee,
    net_amount: NetAmount,
) {}

// or use a struct to group them
struct OrderPayoutParams {
     shop_id: ShopId,
     customer_id: CustomerId,
     order_id: OrderId,
     amount: Amount,
     platform_fee: PlatformFee,
     tx_fee: TxFee,
     net_amount: NetAmount,
}

fn process_order_payout(params: OrderPayoutParams) {}

The compiler only sees types. Two String params are indistinguishable at the call site. A swap compiles, passes review, ships to prod, and breaks in a way that looks like perfectly valid code.

Exceptions
  • Known positional conventions: x, y, z, a, b, lhs, rhs
  • Operator trait impls: fn eq(&self, other: &Self)
  • Functions with only 2 params where names clearly convey order
  • Closure arguments
  • Trait implementations where the signature is fixed by the trait
Comparison with existing lints
  • too_many_arguments flags functions with too many parameters but doesn't care whether the types are interchangeable. A function with 8 parameters of 8 distinct types is fine from a swappability perspective.
  • This lint is orthogonal: even a 2-parameter function like fn transfer(from: String, to: String) is dangerous if both params share a type.
Additional Context

I wrote about this in detail here: https://sot.dev/everything-should-be-typed.html

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No source file or test is named. Start by locating the existing too_many_arguments lint and its tests in rust-clippy, then compare their registration and exception handling with the proposed rules. Done means the new lint is registered, flags the stated interchangeable-parameter cases, skips the listed exceptions, and has coverage for representative examples.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.