42

I am trying to write a simple function in Rust that will ask user a question expecting answer of "you" or "me". It should return a boolean value or ask again if the user answers wrong. I came up with:

fn player_starts() -> bool {                                                    
    println!("Who will start (me/you)");                                       
    loop {                                                                      
        let input = readline::readline(">");                                    
        match input {                                                           
            Some("me") => return true,                                          
            Some("you") => return false,                                        
            _ => None,                                                          
        }                                                                          
    }                                                                           
}       

What I get is:

error: mismatched types:
 expected `collections::string::String`,
    found `&'static str`
(expected struct `collections::string::String`,
found &-ptr) [E0308]

Is there some way to coerce the literal to work here or is there some better way to achieve my goal?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
zefciu
  • 1,967
  • 2
  • 17
  • 39

2 Answers2

41

The way you usually convert a &str to a String is to_owned, e.g.

"me".to_owned()

However, you can't do pattern matching on a String. You could expect a success, get a &str from the String then pattern match on that:

fn player_starts() -> bool {                                                    
    println!("Who will start (me/you)");                                       
    loop {                                                                      
        let input = readline::readline(">");
        match input.expect("Failed to read line").as_ref() {
            "me" => return true,                                          
            "you" => return false,
            _ => println!("Enter me or you"),
        }                                                                          
    }                                                                           
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • 4
    It's more idiomatic to use `to_owned()` or `into()` (in case the target type is known) rather than `to_string()`. The latter works through `Display` trait which invokes formatting code which introduces some overhead. Maybe if Rust gets impl specialization, this could be fixed, but we're not there yet. – Vladimir Matveev Sep 10 '15 at 12:23
  • @VladimirMatveev thanks, still pretty new to Rust, so I guess I shouldn't be making such assertions based on minimal experience. – TartanLlama Sep 10 '15 at 12:30
  • Is the downvote due to the above or are there other issues with this solution? – TartanLlama Sep 10 '15 at 12:35
  • 2
    @TartanLlama My issue with your answer is that it will panic if `input` is `None` which doesn't seem terribly useful to me. (Not downvoting you, though) – fjh Sep 10 '15 at 12:38
  • @TartanLlama I wouldn't go so far as Vladamir to say that to_owned is more idiomatic, there are disagreements among which of the three or four ways are best. – Steve Klabnik Sep 10 '15 at 13:36
23

This should work:

fn player_starts() -> bool {                      
    println!("Who will start me/you)");                    
    loop {
        let input = readline::readline(">");
        match input.as_ref().map(String::as_ref) {
            Some("me") => return true,
            Some("you") => return false,
            _ => ()
        }
    }
}

Note the expression in the match statement, where we convert from an Option<String> to an Option<&str>.

fjh
  • 12,121
  • 4
  • 46
  • 46