Skip to content

diagnostics: suggest iter_mut() where trying to modify .iter()'ed vector elements inplace #62387

Closed

Description

@matthiaskrgr

code:

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

impl A {
    fn double(&mut self) {
        self.a += self.a
    }
}

fn main() {
    let mut v = [A { a: 4 }];
    v.iter().for_each(|a| a.double());
    println!("{:?}", v);
}

The code gives the following not-very-helpful warning:

error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
  --> src/main.rs:14:27
   |
14 |     v.iter().for_each(|a| a.double());
   |                        -  ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |                        |
   |                        help: consider changing this to be a mutable reference: `&mut A`

warning: variable does not need to be mutable
  --> src/main.rs:13:9
   |
13 |     let mut v = [A { a: 4 }];
   |         ----^
   |         |
   |         help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

error: aborting due to previous error

trying to use

v.iter().for_each(|&mut a| a.double());

and a none-mutable vector just causes more errors

error[E0308]: mismatched types
  --> src/main.rs:14:24
   |
14 |     v.iter().for_each(|&mut a| a.double());
   |                        ^^^^^^ types differ in mutability
   |
   = note: expected type `&A`
              found type `&mut _`
   = help: did you mean `mut a: &&A`?

The actual fix is to use iter_mut() instead of just iter():

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

impl A {
    fn double(&mut self) {
        self.a += self.a
    }
}

fn main() {
    let mut v = [A { a: 4 }];
    v.iter_mut().for_each(|a| a.double());
    println!("{:?}", v);
}

It would be very helpful the compiler could suggest iter_mut()!

Activity

  1. matthiaskrgr commented on Jul 4, 2019

    @matthiaskrgr
    MemberAuthor

    @rustbot modify labels: A-diagnostics

  2. rustbot commented on Jul 4, 2019

    @rustbot
    Collaborator

    Error: Parsing label command in comment failed: ...ify labels|error: must have : or to as label starter at >| A-diagnos...

    Please let @rust-lang/release know if you're having trouble with this bot.

  3. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jun 11, 2020
  4. matthiaskrgr commented on Aug 28, 2020

    @matthiaskrgr
    MemberAuthor

    Urgh I just ran into this AGAIN. 😭

    My code was something like this:

    use std::path::PathBuf;
    
    struct Container {
        things: Vec<PathBuf>,
    }
    
    impl Container {
        fn things(&mut self) -> &[PathBuf] {
            &self.things
        }
    }
    
    // contains containers
    struct ContainerContainer {
        contained: Vec<Container>
    }
    
    impl ContainerContainer {
        fn contained(&self ) -> &[Container] {
            &self.contained
        }
    
        fn all_the_things(&mut self) -> &[PathBuf]
        {
           let a = &self.contained().iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
            unimplemented!();
        }
    }
    
    pub fn main() {}

    The error shown:

    error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` reference
      --> src/main.rs:25:62
       |
    25 |        let a = &self.contained().iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
       |                                                   ---------  ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
       |                                                   |
       |                                                   help: consider changing this to be a mutable reference: `&mut Container`
    
    error: aborting due to previous error; 1 warning emitted
    

    So I changed it to |&mut container| and was greeted with

    error[E0308]: mismatched types
      --> src/main.rs:25:51
       |
    25 |        let a = &self.contained().iter().flat_map(|&mut container| container.things()).cloned().collect::<Vec<PathBuf>>();
       |                                                   ^^^^^----------
       |                                                   |    |
       |                                                   |    expected due to this
       |                                                   types differ in mutability
       |                                                   help: did you mean `container`: `&&Container`
       |
       = note:      expected reference `&Container`
               found mutable reference `&mut _`
    
    

    which made me wonder if I was better off with the origin |container|.

    I really wish rust could somehow detect the .iter().bla(|x| fn_that_requires_mut(x) pattern and suggest to use iter_mut() :(

  5. added
    D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.
    on Aug 3, 2023
  6. self-assigned this
    on Aug 27, 2023
  7. added a commit that references this issue on Sep 11, 2023
  8. added a commit that references this issue on Sep 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.T-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