Jeroen
Jeroen

Reputation: 16805

How to match a String against string literals?

I'm trying to figure out how to match a String in Rust.

I initially tried matching like this, but I figured out Rust cannot implicitly cast from std::string::String to &str.

fn main() {
    let stringthing = String::from("c");
    match stringthing {
        "a" => println!("0"),
        "b" => println!("1"),
        "c" => println!("2"),
    }
}

This has the error:

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

I then tried to construct new String objects, as I could not find a function to cast a String to a &str.

fn main() {
    let stringthing = String::from("c");
    match stringthing {
        String::from("a") => println!("0"),
        String::from("b") => println!("1"),
        String::from("c") => println!("2"),
    }
}

This gave me the following error 3 times:

error[E0164]: `String::from` does not name a tuple variant or a tuple struct
 --> src/main.rs:4:9
  |
4 |         String::from("a") => return 0,
  |         ^^^^^^^^^^^^^^^^^ not a tuple variant or struct

How to actually match Strings in Rust?

Upvotes: 448

Views: 214350

Answers (9)

zerocukor287
zerocukor287

Reputation: 1046

As other answers already revealed, String is not str. So basically any function that can convert to an str will do. When reading something from the command line, I especially found useful the trim() method (get rid of the new line, and return an str. As marked in the comments, it does an automatic dereference during the call.

Here is the code I used:

let mut input: String = Default::default();
io::stdin().read_line(&mut input)
           .expect("Something bad happened");
match input.trim() {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

Upvotes: 0

Ayoub BOUMZEBRA
Ayoub BOUMZEBRA

Reputation: 4993

In Rust, you typically cannot use a String directly in a match statement because String is a dynamic, heap-allocated data type, and match expects values with a known and fixed set of possibilities at compile time.

However, you can use a String in a match statement by first converting it to a &str using the as_str() method or by using pattern matching with a reference to the String.

match expects exhaustive and known patterns: The match expression requires you to specify all possible patterns at compile time. String literals are known at compile time, so you can use them directly in a match. On the other hand, the content of a String is determined at runtime and can vary, so it cannot be directly used in a match.

Upvotes: 1

rujen gelal
rujen gelal

Reputation: 13

Use as_str() on Strings to get string slice

fn main() {
    let stringthing = String::from("c");
    match stringthing.as_str() {
        String::from("a") => println!("0"),
        String::from("b") => println!("1"),
        String::from("c") => println!("2"),
    }
}

if your taking input from the console and want to perform match on it be sure to call trim() after as_str() to remove escape character i.e '\n' from the input. As in

match stringthing.as_str().trim() {...}


Upvotes: -2

Tijs Maas
Tijs Maas

Reputation: 4533

UPDATE: Use .as_str() like this to convert the String to an &str:

match stringthing.as_str() {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

Reason .as_str() is more concise and enforces stricter type checking. The trait as_ref is implemented for multiple types and its behaviour could be changed for type String, leading to unexpected results. Similarly, if the input argument changes type, the compiler will not signal a problem when that type implements the trait as_ref.

The docs suggest to use as_str as well https://doc.rust-lang.org/std/string/struct.String.html, https://doc.rust-lang.org/std/primitive.str.html

Old answer:

as_slice is deprecated, you should now use the trait std::convert::AsRef instead:

match stringthing.as_ref() {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

Note that you also have to explicitly handle the catch-all case.

Upvotes: 433

crimsondamask
crimsondamask

Reputation: 51

You can convert the String into &str by doing this:

fn main() {
    let stringthing = String::from("c");
    match &stringthing[..] {
        "a" => println!("0"),
        "b" => println!("1"),
        "c" => println!("2"),
    }
}

Upvotes: 1

Anonymous Coward
Anonymous Coward

Reputation: 1996

You can do something like this:

match &stringthing[..] {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

There's also an as_str method as of Rust 1.7.0:

match stringthing.as_str() {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

Upvotes: 198

omrihhh
omrihhh

Reputation: 89

You can try:

fn main() {
    let stringthing = String::from("c");
    match &*stringthing {
        "a" => println!("0"),
        "b" => println!("1"),
        "c" => println!("2"),
        _ => println!("else")
    }
}

Upvotes: 3

A.B.
A.B.

Reputation: 16630

Editor's note: This answer pertains to an version of Rust before 1.0 and does not work in Rust 1.0

You can match on a string slice.

match stringthing.as_slice() {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

Upvotes: 11

Marco Scannadinari
Marco Scannadinari

Reputation: 1874

You could also do

match &stringthing as &str {
    "a" => println!("0"),
    "b" => println!("1"),
    "c" => println!("2"),
    _ => println!("something else!"),
}

See:

Upvotes: 26

Related Questions