Skip to content

Suggests removed "unused" mut when not actually able to be removed #11199

@emmiegit

Description

@emmiegit

Summary

Clippy will suggest that mut be removed from function references if it is not used, even if the mutability cannot be removed because of function type requirements.

Lint Name

clippy::needless_pass_by_ref_mut

Reproducer

The codebase this occurs in is rather complicated, so I reduced it to a simple test case which reproduces the same false positive.

#[derive(Debug)]
struct Context {
    a: i32,
    b: i32,
}

type ActionFn = fn(&mut Context);

#[derive(Debug)]
struct Action {
    name: &'static str,
    call: ActionFn,
}

fn action_foo(ctx: &mut Context) {
    println!("Uses r/w access to parameter.");
    ctx.a += 1;
}

fn action_bar(ctx: &mut Context) {
    println!("Only needs r/o access, but must be &mut to match the function definition.");
    let _ = ctx.a * ctx.b;
}

fn main() {
    let action1 = Action {
        name: "Foo",
        call: action_foo,
    };

    let action2 = Action {
        name: "Bar",
        call: action_bar,
    };

    run_action(&action1);
    run_action(&action2);
}

fn run_action(action: &Action) {
    println!("Running action {}", action.name);
    let mut ctx = Context { a: 100, b: -20 };
    (action.call)(&mut ctx);
}

Here we have a fixed object Action which expects a specific type for its function pointer. However, while most implementers need for full mutable access, some do not, which then triggers the following Clippy lint:

warning: this argument is a mutable reference, but not used mutably
  --> src/main.rs:20:20
   |
20 | fn action_bar(ctx: &mut Context) {
   |                    ^^^^^^^^^^^^ help: consider changing to: `&Context`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
   = note: `#[warn(clippy::needless_pass_by_ref_mut)]` on by default

warning: `test2` (bin "test2") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s

However, this is incorrect, as the mut cannot actually be removed, as this will change the function signature, causing a compiler error. That is, if I change line 20 to fn action_bar(ctx: &Context), then I get:

    Checking test2 v0.1.0 (/tmp/emmie/test2)
error[E0308]: mismatched types
  --> src/main.rs:33:15
   |
33 |         call: action_bar,
   |               ^^^^^^^^^^ types differ in mutability
   |
   = note: expected fn pointer `for<'a> fn(&'a mut Context)`
                 found fn item `for<'a> fn(&'a Context) {action_bar}`
   = note: when the arguments and return types match, functions can be coerced to function pointers

For more information about this error, try `rustc --explain E0308`.
error: could not compile `test2` (bin "test2") due to previous error

Because there is a legitimate type incompatibility reason why mut cannot be removed here, Clippy should not be recommending its removal.

Version

rustc 1.73.0-nightly (39f42ad9e 2023-07-19)
binary: rustc
commit-hash: 39f42ad9e8430a8abb06c262346e89593278c515
commit-date: 2023-07-19
host: x86_64-unknown-linux-gnu
release: 1.73.0-nightly
LLVM version: 16.0.5

clippy 0.1.73 (39f42ad 2023-07-19)

Additional Labels

@rustbot label +I-suggestion-causes-error

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't haveI-suggestion-causes-errorIssue: The suggestions provided by this Lint cause an ICE/error when applied

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions