51

I'm trying to implement a function that reads command line arguments and compares them to hard-coded string literals.

When I do the comparison with an if statement it works like a charm:

fn main() {
    let s = String::from("holla!");

    if s == "holla!" {
        println!("it worked!");
    }
}

But using a match statement (which I guess would be more elegant):

fn main() {
    let s = String::from("holla!");

    match s {
        "holla!" => println!("it worked!"),
        _ => println!("nothing"),
    }
}

I keep getting an error from the compiler that a String was expected but a &static str was found:

error[E0308]: mismatched types
 --> src/main.rs:5:9
  |
5 |         "holla!" => println!("it worked!"),
  |         ^^^^^^^^ expected struct `std::string::String`, found reference
  |
  = note: expected type `std::string::String`
             found type `&'static str`

I've seen How to match a String against string literals in Rust? so I know how to fix it, but I want to know why the comparison works when if but not using match.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
robfuscator
  • 631
  • 1
  • 5
  • 13
  • Because a `&str` is a reference, not a value. – AJF Apr 17 '18 at 19:38
  • 1
    @Shepmaster Thanks for pointing out my mistakes. The question you have linked is very similar to mine, but I like to know why it works with the `if` comparison but not with a `match`. – robfuscator Apr 17 '18 at 20:41
  • 5
    It is simply because the compiler handles them both differently. The `if` statement delegates to the `PartialEq` implementation. The `match` doesn't have this special handling in the compiler and therefore requires a bit of help. The two constructs are compiled differently anyway so I'm not sure how this would work for the internals of the compiler. The `if` conditional becomes a simple branch whereas the `match` does appear (according to the LLVM IR) to be somewhat of a `switch` when compiled down. – Simon Whitehead Apr 18 '18 at 01:00
  • If there are just two possible outcomes (like in your second example), an `if else` is a perfectly fine construct to use. – ljedrz Apr 18 '18 at 06:35

2 Answers2

74

I want to know why the comparison works when if but not using match.

It's not so much about if and more because you've used == in the condition. The condition in an if statement is any expression of type bool; you just happen to have chosen to use == there.

The == operator is really a function associated with the PartialEq trait. This trait can be implemented for any pair of types. And, for convenience, String has implementations for PartialEq<str> and PartialEq<&str>, among others - and vice versa.

On the other hand, match expressions use pattern matching for comparison, not ==. A &'static str literal, like "holla!", is a valid pattern, but it can never match a String, which is a completely different type.

Pattern matching lets you concisely compare parts of complex structures, even if the whole thing isn't equal, as well as bind variables to pieces of the match. While Strings don't really benefit from that, it's very powerful for other types, and has an entirely different purpose than ==.

Note that you can use pattern matching with if by instead using the if let construct. Your example would look like this:

if let "holla!" = &*s {
    println!("it worked!");
}

Conversely, one way to use == inside a match is like this:

match s {
    _ if s == "holla!" => println!("it worked!"),
    _ => println!("nothing"),
}

Or, as @ljedrz suggested:

match s == "holla!" {
    true => println!("it worked!"), 
    _ => println!("nothing")
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
17

As @peter-hall said, there's a mismatch of types because match expressions use pattern matching, which is different from the == that are associated with the PartialEq trait.

There a second way to resolve this issue, by casting your String into an &str (a string slice) :

match &s[..] {
    "holla!" => println!("it worked!"),
    "Hallo!" => println!("with easy to read matches !"),
    _ => println!("nothing"),
}
Naeio
  • 1,112
  • 7
  • 21
  • 2
    top answer! As match is special and String is special, we wish the compiler would do that for us. – Sam Liddicott May 30 '22 at 10:00
  • 1
    @SamLiddicott The problem is that the core language doesn't define String, so making a special case for it is not ideal. So that would mean having a general way to do this. It could be through a trait that is used whenever there's a match, I'm just not sure of the implications of doing this. – Naeio Apr 26 '23 at 09:34