23

I'm new to Rust (1.31) and I would like to understand a simple piece of code that does not compile:

fn main() {
    s = String::from("foo");
    match s {
        "foo" => {
            println!("Yes");
        }
        _ => {
            println!("No");
        }
    }
}

The error associated is :

10 |         "foo" => {                                                                                 
   |         ^^^^^ expected struct `std::string::String`, found reference

After this error, I decided to change the code to :

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

    match s {
        String::from("foo") => {
            println!("Yes");
        }
        _ => {
            println!("No");
        }
    }
}

By doing so, I was hoping to have the correct type, but it is not the case :

10 |         String::from("foo") => {                                                                   
   |         ^^^^^^^^^^^^^^^^^^^ not a tuple variant or struct

I am quite puzzled with this message from the compiler, at the end I managed to make it work by implementing :

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

    match &s as &str {
        "foo" => {
            println!("Yes");
        }
        _ => {
            println!("No");
        }
    }
}

However, I do not understand the underlying mechanisms that make this solution the right one and why my second example does not work.

Coding thermodynamist
  • 1,340
  • 1
  • 10
  • 18
  • The left hand side of a match arm is a pattern. It can't include things like function calls (such as `String::from`). The literal `"foo"` is a pattern. – Peter Hall Dec 22 '18 at 21:34
  • The reason why the last attempt works is because `String` dereferences to `str`. That is, `String` implements the `Deref` trait with `str` as its output type. – Peter Hall Dec 22 '18 at 21:37

1 Answers1

29

The first example doesn't work, because s is of type String, which is a string variant that owns the data in it. It is matched against a string literal (which can be be used as type &str). match doesn't understand how to compare those two different types, so it errors.

However String dereferences to &str, by implementing Deref<Target=str>, which means references to String can be used where a &str is required, e.g. for comparing it to one. That's what happens in the third example. By creating the reference &s, the implicit deref can happen, and the two types get comparable.

You can achieve the same thing with a little less magic by using the explicit method which creates a &str from String:

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

    match s.as_str() {
        "foo" => {
            println!("Yes");
         },
         _ => {
             println!("No");
         }
    }
}

The second example tries to make things comparable making String the common type instead of &str. It doesn't work, because match expects a pattern on the left side, and not a function call which creates a new struct (and which also allocates behind the scene). Even if it would work (e.g. by moving the String creation outside of the match), the approach would be less desirable, because the new String would require a memory allocation.

Matthias247
  • 9,836
  • 1
  • 20
  • 29