Skip to content

Unhelpful suggestions on reference mutability #134467

Open
@Walther

Description

@Walther

This code example is minimized from a wild goose chase I ran into when implementing a solution for one of the Advent of Code puzzles.

The error messages and suggestions were not helpful in solving the issue. Some of them were actively confusing - from the programmer's perspective, the variables clearly need mutability in some form - yet the compiler kept insisting the mut keywords should be removed.

Code

fn main() {
    // const INPUT: &str = include_str!("input.txt");
    const INPUT: &str = r#"Hello
Hola
Bonjour
Ciao
"#;
    let parsed = parse(INPUT);

    let value = part1(&parsed);
    println!("Part 1: {value}");

    let value = part2(&parsed);
    println!("Part 2: {value}");
}

struct Ferris {
    greeting: String,
    count: usize,
}

impl Ferris {
    pub fn new(greeting: &str) -> Self {
        Ferris {
            greeting: greeting.to_string(),
            count: 0,
        }
    }

    pub fn greet(&mut self) {
        self.count += 1;
        println!("{}", self.greeting);
    }

    pub fn greet_count(&self) -> usize {
        self.count
    }
}

type ParsedData = Vec<Ferris>;

fn parse(input: &str) -> ParsedData {
    input.lines().map(Ferris::new).collect()
}

fn part1(data: &ParsedData) -> usize {
    let mut crabs = data.clone();
    for crab in crabs {
        crab.greet();
    }
    crabs.iter().map(Ferris::greet_count).sum()
}

fn part2(_data: &ParsedData) -> usize {
    0
}

Current output

warning: variable does not need to be mutable
  --> src/main.rs:47:9
   |
47 |     let mut crabs = data.clone();
   |         ----^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0596]: cannot borrow `*crab` as mutable, as it is behind a `&` reference
  --> src/main.rs:49:9
   |
48 |     for crab in crabs {
   |                 ----- this iterator yields `&` references
49 |         crab.greet();
   |         ^^^^ `crab` is a `&` reference, so the data it refers to cannot be borrowed as mutable

For more information about this error, try `rustc --explain E0596`.

Other cases

Remove mut as instructed:

fn part1(data: &ParsedData) -> usize {
    let crabs = data.clone();
    for crab in crabs {
        crab.greet();
    }
    crabs.iter().map(Ferris::greet_count).sum()
}
error[E0596]: cannot borrow `*crab` as mutable, as it is behind a `&` reference
  --> src/main.rs:49:9
   |
48 |     for crab in crabs {
   |                 ----- this iterator yields `&` references
49 |         crab.greet();
   |         ^^^^ `crab` is a `&` reference, so the data it refers to cannot be borrowed as mutable

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

You know this should be mutable, and the error talks about references, so let's try &mut instead:

fn part1(data: &ParsedData) -> usize {
    let mut crabs = data.clone();
    for &mut crab in crabs {
        crab.greet();
    }
    crabs.iter().map(Ferris::greet_count).sum()
}
error[E0308]: mismatched types
  --> src/main.rs:48:9
   |
48 |     for &mut crab in crabs {
   |         ^^^^^^^^^    ----- this is an iterator with items of type `&Ferris`
   |         |
   |         types differ in mutability
   |
   = note:      expected reference `&Ferris`
           found mutable reference `&mut _`

For more information about this error, try `rustc --explain E0308`.

Maybe use the &mut in a different place then?

fn part1(data: &ParsedData) -> usize {
    let mut crabs = data.clone();
    for crab in &mut crabs {
        crab.greet();
    }
    crabs.iter().map(Ferris::greet_count).sum()
}
error[E0277]: `&Vec<Ferris>` is not an iterator
  --> src/main.rs:48:17
   |
48 |     for crab in &mut crabs {
   |                 ^^^^^^^^^^ `&Vec<Ferris>` is not an iterator
   |
   = help: the trait `Iterator` is not implemented for `&Vec<Ferris>`, which is required by `&mut &Vec<Ferris>: IntoIterator`
   = note: required for `&mut &Vec<Ferris>` to implement `Iterator`
   = note: required for `&mut &Vec<Ferris>` to implement `IntoIterator`
help: consider removing the leading `&`-reference
   |
48 -     for crab in &mut crabs {
48 +     for crab in crabs {
   |

For more information about this error, try `rustc --explain E0277`.

Rust Version

rustc 1.83.0 (90b35a623 2024-11-26)
binary: rustc
commit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf
commit-date: 2024-11-26
host: aarch64-apple-darwin
release: 1.83.0
LLVM version: 19.1.1

Anything else?

Here's the actual fix, hidden under a spoiler if anyone wants to try and figure it out first by themselves:

Solution
#[derive(Clone)] // <- this missing Clone derive was the real cause all along!
struct Ferris {
    greeting: String,
    count: usize,
}

fn part1(data: &ParsedData) -> usize {
    let mut crabs = data.clone();
    for crab in &mut crabs {
        crab.greet();
    }
    crabs.iter().map(Ferris::greet_count).sum()
}

Additionally, a friend pointed out to me that using for crab in crabs.iter_mut() produces a helpful error message.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions