Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusing compilation error when forgetting to deref_mut in a chain of iterators #105337

Open
robinmoussu opened this issue Dec 5, 2022 · 9 comments
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@robinmoussu
Copy link

robinmoussu commented Dec 5, 2022

This doesn’t compile:

fn foo(items: &mut [u8]) -> u8 {
    // The centent of this function isn’t important
    items.sort();
    42
}

fn main() {
    let mut x: Vec<Vec<u8>> = vec![
        vec![0, 1, 2],
        vec![3, 4, 5],
    ];
    let x = x
        .iter_mut()
        .map(foo) // <-- this line
    println!("{:?}", x);
}
error[[E0631]](https://doc.rust-lang.org/stable/error-index.html#E0631): type mismatch in function arguments
  --> src/main.rs:20:14
   |
1  | fn foo(items: &mut [u8]) -> u8 {
   | ------------------------------ found signature defined here
...
20 |         .map(foo);
   |          --- ^^^ expected due to this
   |          |
   |          required by a bound introduced by this call
   |
   = note: expected function signature `fn(&mut Vec<u8>) -> _`
              found function signature `for<'r> fn(&'r mut [u8]) -> _`
note: required by a bound in `map`

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

The solution is either to call first deref

.map(std::ops::DerefMut::deref_mut).map(foo)

or

.map(|items| items.as_mut())

Or to use a lambda

.map(|mut items| foo(&mut items))

The current error doesn’t help to understand what the issue is and it’s very confusing because it may look like a lifetime error (because of the for<'r>…).

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c6ac2ca7a31b126442978d30419e07a8

@robinmoussu robinmoussu added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Dec 5, 2022
@estebank estebank added D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Dec 5, 2022
@ch-iv
Copy link
Contributor

ch-iv commented Dec 6, 2022

@rustbot claim

@robinmoussu
Copy link
Author

Trying to force the deref using from is equally as confusing:

    let x = x
        .iter_mut()
        .map(<&mut [u8]>::from) // <-- this line
        .map(foo);
error[[E0631]](https://doc.rust-lang.org/stable/error-index.html#E0631): type mismatch in function arguments
  --> src/main.rs:18:14
   |
18 |         .map(<&mut [u8]>::from)
   |          --- ^^^^^^^^^^^^^^^^^
   |          |   |
   |          |   expected due to this
   |          |   found signature defined here
   |          required by a bound introduced by this call
   |
   = note: expected function signature `fn(&mut Vec<u8>) -> _`
              found function signature `fn(&mut [u8]) -> _`
note: required by a bound in `map`

@ch-iv
Copy link
Contributor

ch-iv commented Dec 9, 2022

As I understand, the issue is that x.iter_mut() returns an IterMut and when iterating over it, the elements are references. In order to fix the error, the elements must be dereferenced first, for example, by calling .map(std::ops::DerefMut::deref_mut). The error and notes don't give any indication of that. Is my understanding correct?

@ch-iv
Copy link
Contributor

ch-iv commented Dec 20, 2022

I couldn't find confirmation, so I am releasing.
@rustbot release-assignment

@robinmoussu
Copy link
Author

As I understand, the issue is that x.iter_mut() returns an IterMut and when iterating over it, the elements are references. In order to fix the error, the elements must be dereferenced first, for example, by calling .map(std::ops::DerefMut::deref_mut). The error and notes don't give any indication of that. Is my understanding correct?

@ch-iv Your understanding is right. Sorry, I did not saw the notification, which explain why I didn’t responded earlier.

@ch-iv
Copy link
Contributor

ch-iv commented Dec 21, 2022

@rustbot claim

@estebank
Copy link
Contributor

estebank commented Jan 7, 2023

@ch-iv you can take a look at #105674 for inspiration on how to walk the method chain looking for the previous types, but in this case you need to look at the expected/found arguments, detect there's a single one and then check if the found implements Deref or DerefMut into the expected type, and then suggest the map(appropriate::method).

@ch-iv
Copy link
Contributor

ch-iv commented Jan 7, 2023

@estebank Ok, thank you. I was unsure about how to proceed, but I hope this will help.

@estebank
Copy link
Contributor

estebank commented Oct 4, 2024

Current output:

error[E0631]: type mismatch in function arguments
   --> src/main.rs:14:14
    |
1   | fn foo(items: &mut [u8]) -> u8 {
    | ------------------------------ found signature defined here
...
14  |         .map(foo); // <-- this line
    |          --- ^^^ expected due to this
    |          |
    |          required by a bound introduced by this call
    |
    = note: expected function signature `fn(&mut Vec<u8>) -> _`
               found function signature `fn(&mut [u8]) -> _`
note: required by a bound in `map`
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:765:12
    |
762 |     fn map<B, F>(self, f: F) -> Map<Self, F>
    |        --- required by a bound in this associated function
...
765 |         F: FnMut(Self::Item) -> B,
    |            ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Iterator::map`
help: consider wrapping the function in a closure
    |
14  |         .map(|arg0: &mut Vec<u8>| foo(/* &mut [u8] */)); // <-- this line
    |              ++++++++++++++++++++    +++++++++++++++++

The suggestion isn't perfect, but it provides a path to get to working code:

error[E0596]: cannot borrow `arg0` as mutable, as it is not declared as mutable
  --> src/main.rs:14:39
   |
14 |         .map(|arg0: &mut Vec<u8>| foo(&mut arg0)); // <-- this line
   |                                       ^^^^^^^^^ cannot borrow as mutable
   |
note: the binding is already a mutable borrow
  --> src/main.rs:14:21
   |
14 |         .map(|arg0: &mut Vec<u8>| foo(&mut arg0)); // <-- this line
   |                     ^^^^^^^^^^^^
help: try removing `&mut` here
   |
14 -         .map(|arg0: &mut Vec<u8>| foo(&mut arg0)); // <-- this line
14 +         .map(|arg0: &mut Vec<u8>| foo(arg0)); // <-- this line
   |

After passing arg0 directly, the code compiles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants