Skip to content

E0277 error is confusing when compile fails using the last implementor for a trait #56368

Closed
@ronjouch

Description

@ronjouch

Description

This is a followup of forum post [Solved] Need help understanding compilation error with str.contains / String / Pattern.

Here's a function that compiles:

fn compiles() -> bool {
    let word = String::from("hello");
    "hello world".contains(&word)
}

But if I omit passing a reference to contains and pass the actual String:

fn wont_compile() -> bool {
    let word = String::from("hello");
    "hello world".contains(word) // <- missing "&" here before "word"
}

, then compilation fails with error

error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String`        
  --> src/lib.rs:96:19                                                                          
   |                                                                                            
96 |     "hello world".contains(word)                                                           
   |                   ^^^^^^^^ expected an `FnMut<(char,)>` closure, found `std::string::String`
   |                                                                                            
   = help: the trait `std::ops::FnMut<(char,)>` is not implemented for `std::string::String`    
   = note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`

In this context, as a babbling Rustacean, I had zarroo idea of what rustc was trying to tell me and, assuming the solution (which I found by looking up a .contains() example in the Rust book, where a & reference was used), my incorrect concluding puzzlement was:

  • How a String isn't a std::ops::FnMut<(char,)>, but a reference to a String is?
  • And what is a closure even doing here, where we're all talking Strings?

As pointed by forum user krdln, what's happening is that, as documented by str.contains, I should have passed a Pattern, whose 6 implementors are:

impl<'a> Pattern<'a> for char
impl<'a, 'b> Pattern<'a> for &'b str
impl<'a, 'b> Pattern<'a> for &'b String
impl<'a, 'b> Pattern<'a> for &'b [char]
impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str
impl<'a, F> Pattern<'a> for F where F: FnMut(char) -> bool

, so rustc's logic behind the error message was:

"You’ve passed me something that is not a char, &str, &String, &[char] nor &&str, so it must be some other type F! If you want an F to be a pattern, it must implement FnMut!"

Proposal

When rustc fails to compile using the last (fallback? generic?) implementor for a trait,

  1. Warn that it reached the last implementor.
  2. List the missed implementors (always? If short enough? Abbreviated if too long? Only the count if too long? I don't know).

The updated, friendlier error message could look something like that:

error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String`        
  --> src/lib.rs:96:19                                                                          
   |                                                                                            
96 |     "hello world".contains(word)                                                           
   |                   ^^^^^^^^ expected an `FnMut<(char,)>` closure, found `std::string::String`
   |                                                                                            
   = help: the trait `std::ops::FnMut<(char,)>` is not implemented for `std::string::String`    
// START ADDITION PROPOSAL =========================================
   = help: reached last generic implementor for `std::str::pattern::Pattern<'_>`, maybe you meant to use one of the 5 other implementors:
           char, &'b str, &'b String, &'b [char], &'c &'b str
// END ADDITION PROPOSAL   =========================================
   = note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`

Note: hope the proposal makes sense, or at least you get the intention! I have no idea what I'm doing! This is my first Rust bug! I haven't even finished the Rust book! One last exclamation mark for the road!

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsD-confusingDiagnostics: Confusing error or lint that should be reworked.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