How can I pattern match against an Option?

后端 未结 4 1006
臣服心动
臣服心动 2021-01-01 10:13

I can straight-forwardly match a String in Rust:

let a = \"hello\".to_string();

match &a[..] {
    \"hello\" => {
        println!(\"Mat         


        
相关标签:
4条回答
  • 2021-01-01 10:53

    In some cases, you can use unwrap_or to replace Option::None with a predefined &str you don't want to handle in any special way.

    I used this to handle user inputs:

    let args: Vec<String> = env::args().collect();
    
    match args.get(1).unwrap_or(&format!("_")).as_str() {
        "new" => {
            print!("new");
        }
        _ => {
            print!("unknown string");
        }
    };
    

    Or to match your code:

    let option = Some("hello");
    
    match option.unwrap_or(&format!("unhandled string").as_str()) {
        "hello" => {
            println!("hello");
        }
        _ => {
            println!("unknown string");
        }
    };
    
    0 讨论(0)
  • 2021-01-01 10:57

    As of Rust 1.40, you can now use as_deref instead of the top answers:

    match args.nth(1).as_deref() {
        Some("help") => {}
        Some(s) => {}
        None => {}
    }
    

    I found this because it is one of the clippy lints.

    0 讨论(0)
  • 2021-01-01 10:58

    Look at this.

    You cannot match on std::String, as you've found, only on &str. Nested pattern matches work, so if you can match on &str, you can match on Option<&str>, but still not on Option<String>.

    In the working example, you turned the std::String into a &str by doing &a[..]. If you want to match on a Option<String>, you have to do the same thing.

    One way is to use nested matches:

    match a {
        Some(ref s) => match &s[..] {
            "hello" => /* ... */,
            _ => /* ... */,
        },
        _ => /* ... */,
    }
    

    But then you have to duplicate the "otherwise" code if it's the same, and it's generally not as nice.

    Instead, you can turn the Option<String> into an Option<&str> and match on this, using the map function. However, map consumes the value it is called on, moving it into the mapping function. This is a problem because you want to reference the string, and you can't do that if you have moved it into the mapping function. You first need to turn the Option<String> into a Option<&String> and map on that.

    Thus you end up with a.as_ref().map(|s| /* s is type &String */ &s[..]). You can then match on that.

    match os.as_ref().map(|s| &s[..]) {
        Some("hello") => println!("It's 'hello'"),
        // Leave out this branch if you want to treat other strings and None the same.
        Some(_) => println!("It's some other string"),
        _ => println!("It's nothing"),
    }
    
    0 讨论(0)
  • 2021-01-01 10:59

    It's a known limitation of Rust's patterns.

    Method calls (including internal methods for operators like ==) automatically call .deref() as needed, so String gets automagically turned into &str for comparisons with literals.

    On the other hand, the patterns are quite literal in their comparisons, and find that String and &str are different.

    There are two solutions:

    1. Change Option<String> to Option<&str> before matching on it: Some(a).as_ref().map(String::as_str). The as_ref() makes Option<&String> (preventing move), and as_str() then unambiguously references it as a &str.

    2. Use match guard: match Some(a) { Some(ref s) if s == "hello" => … }. Some(ref s) matches any String, and captures it as s: &String, which you can then compare in the if guard which does the usual flexible coercions to make it work.

    See also:

    • Converting from Option<String> to Option<&str>
    0 讨论(0)
提交回复
热议问题