[Solved] Need help understanding compilation error with str.contains / String / Pattern


#1

Here’s a function that compiles:

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

Good. Now, 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`

Usually rustc is pretty helpful, but here I have zarroo idea of what it’s trying to tell me and, assuming the solution, I don’t understand how a String isn’t a std::ops::FnMut<(char,)>, but a reference to a String is. What is a closure even doing here? The only reason I found my error was that I looked up a contains example from the rust book. Can you show me how I c/should have jumped that hoop properly?

  • Using rustc output: what’s going on with the above-quoted error message (or in rustc --explain E0277) ? What should have put me on the right track and I missed?
  • Using the docs: docs for str.contains say I should pass a Pattern. What here explicits that a String isn’t a Pattern?

Finally, why doesn’t / couldn’t the String type implement the Pattern trait, and why does a reference to a String can?

Thanks for the help :slightly_smiling_face:.


#2

On the Pattern page you’ve linked, there’s a list of implementors (note that String is not there, only &String):

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,

The compiler’s “logic” behind the error message is:

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!

I agree that the error message here is confusing. On the other hand, list of all the trait implementors could get long, so printing absolutely every type in every case won’t fly. Anyway, I suggest to file an issue describing how the message is misleading (or even, what could be done to help, if you have any ideas)

My guess would be to discourage “destroying still a good string if you can just borrow it instead” :slight_smile:


#3

@krdln yay thanks, makes total sense! I followed your suggestion to file an issue and filed my first rust bug :slightly_smiling_face:, E0277 error is confusing when compile fails using the last implementor for a trait.

Good day/night.