简体   繁体   English

如何对选项进行模式匹配<String> ?

[英]How can I pattern match against an Option<String>?

I can straight-forwardly match a String in Rust:我可以直接匹配 Rust 中的String

let a = "hello".to_string();

match &a[..] {
    "hello" => {
        println!("Matches hello");
    }
    _ => panic!(),
}

If I have an option type, it fails:如果我有一个选项类型,它会失败:

match Some(a) {
    Some("hello") => {
        println!("Matches some hello");
    }
    _ => panic!(),
}

because the types don't match:因为类型不匹配:

error[E0308]: mismatched types
 --> src/main.rs:5:14
  |
5 |         Some("hello") => {
  |              ^^^^^^^ expected struct `std::string::String`, found reference
  |
  = note: expected type `std::string::String`
             found type `&'static str`

I can't do the [..] trick because we have an Option .我不能做[..]把戏,因为我们有一个Option The best that I have come up with so far is:到目前为止,我想出的最好的是:

match Some(a) {
    Some(b) => match (&b[..]) {
        "hello" => {
            println!("Matches some, some hello");
        }
        _ => panic!(),
    },
    None => panic!(),
}

which works but is terrible for its verbosity.这有效,但因其冗长而可怕。

In this case, my code is just an example.在这种情况下,我的代码只是一个例子。 I do not control the creation of either the String or the Some(String) — so I can't change this type in reality as I could do in my example.我不控制StringSome(String) ——所以我不能像在我的例子中那样在现实中改变这种类型。

Any other options?还有其他选择吗?

It's a known limitation of Rust's patterns.这是 Rust 模式的一个已知限制。

Method calls (including internal methods for operators like == ) automatically call .deref() as needed, so String gets automagically turned into &str for comparisons with literals.方法调用(包括==运算符的内部方法.deref()根据需要自动调用.deref() ,因此String会自动转换为&str以与文字进行比较。

On the other hand, the patterns are quite literal in their comparisons, and find that String and &str are different.另一方面,模式在它们的比较中非常直接,并且发现String&str是不同的。

There are two solutions:有两种解决方案:

  1. Change Option<String> to Option<&str> before matching on it: Some(a).as_deref() .在匹配之前将Option<String>更改为Option<&str>Some(a).as_deref() The as_deref() is a combo of as_ref() that makes Option<&String> (preventing move), and deref() / as_str() then unambiguously references it as a &str .所述as_deref()是一个组合as_ref()使Option<&String> (防止移动),和deref() / as_str()然后明确地引用它作为一个&str

  2. Use match guard: match Some(a) { Some(ref s) if s == "hello" => … } .使用匹配保护: 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. Some(ref s)匹配任何String ,并将其捕获为s: &String ,然后您可以在if守卫中进行比较,该守卫执行通常的灵活强制以使其工作。

See also:另见:

As of Rust 1.40, you can now call as_deref on Option<String> to convert it to Option<&str> and then match on it:从 Rust 1.40 开始,您现在可以在Option<String>上调用as_deref将其转换为Option<&str> ,然后对其进行匹配:

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

I found this because it is one of the clippy lints .我发现了这个,因为它是一种可剪的 lints

Look at this . 看看这个

You cannot match on std::String , as you've found, only on &str .正如您所发现的,您无法在std::String上匹配,只能在&str上匹配。 Nested pattern matches work, so if you can match on &str , you can match on Option<&str> , but still not on Option<String> .嵌套模式匹配有效,因此如果您可以匹配&str ,则可以匹配Option<&str> ,但仍然不能匹配Option<String>

In the working example, you turned the std::String into a &str by doing &a[..] .在工作示例中,您通过执行&a[..]std::String转换为&str If you want to match on a Option<String> , you have to do the same thing.如果你想匹配Option<String> ,你必须做同样的事情。

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.相反,您可以使用map函数将Option<String>转换为Option<&str>并对其进行匹配。 However, map consumes the value it is called on, moving it into the mapping function.然而, map消耗了它被调用的值,将它移动到映射函数中。 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.您首先需要将Option<String>转换为Option<&String>并对其进行映射。

Thus you end up with a.as_ref().map(|s| /* s is type &String */ &s[..]) .因此你最终得到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"),
}

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.在某些情况下,您可以使用unwrap_orOption::None替换为您不想以任何特殊方式处理的预定义&str

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");
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM