如何模式匹配Option <string>?

时间:2017-12-30 12:54:04

标签: rust pattern-matching

我可以直接匹配Rust中的String

let a = "hello".to_string();

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

如果我有选项类型,则会失败:

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

因为类型不匹配:

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`

我无法执行[..]技巧,因为我们有一个Option。最好的 到目前为止,我想出的是:

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

虽然有效,但其冗长程度很糟糕。

在这种情况下,我的代码只是一个例子。我无法控制StringSome(String)的创建 - 所以我无法在实际中更改此类型,就像我在我的示例中所做的那样。

还有其他选择吗?

4 个答案:

答案 0 :(得分:9)

这是Rust的模式的已知限制。

方法调用(包括==等运算符的内部方法)会根据需要自动调用.deref(),因此String会自动转换为&str,以便与文字进行比较。

OTOH这些模式在比较中非常直观,并且发现String&str不同。

有两种解决方案:

  1. 在匹配之前将Option<String>更改为Option<&str>Some(a).as_ref().map(String::as_str)as_ref()生成Option<&String>(阻止移动),然后as_str()明确地将其引用为&str

  2. 使用比赛后卫:match Some(a) { Some(ref s) if s == "hello" => … }Some(ref s)String匹配,并将其捕获为s: &String,然后您可以在执行常规灵活强制操作的if后卫中对其进行比较。

  3. 另见:

答案 1 :(得分:9)

从 Rust 1.40 开始,您现在可以在 as_deref 上调用 Option<String> 将其转换为 Option<&str>,然后对其进行匹配:

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

我发现这个是因为它是 one of the clippy lints

答案 2 :(得分:6)

Look at this

您发现的std::String无法与&str匹配。嵌套模式匹配有效,因此如果您可以在&str上匹配,则可以在Option<&str>上匹配,但仍然不在Option<String>上。

在工作示例中,您通过执行std::String&str变为&a[..]。如果你想在Option<String>上匹配,你必须做同样的事情。

一种方法是使用嵌套匹配:

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

但是如果相同的话,你必须复制“其他”代码,而且它通常不那么好。

相反,您可以使用Option<String>功能将Option<&str>变为map并与之匹配。但是,map会消耗它所调用的值,将其移动到映射函数中。这是一个问题,因为您想引用字符串,如果已将其移动到映射函数中,则无法执行此操作。您首先需要将Option<String>转换为Option<&String>并对其进行映射。

因此,您最终得到a.as_ref().map(|s| /* s is type &String */ &s[..])。然后你可以匹配。

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

答案 3 :(得分:0)

在某些情况下,您可以使用unwrap_orOption::None替换为您不希望以任何特殊方式处理的预定义&str

我用它来处理用户输入:

let args: Vec<String> = env::args().collect();

match args.get(1).unwrap_or(&format!("_")).as_str() {
    "new" => {
        print!("new");
    }
    _ => {
        print!("unknown string");
    }
};

或者匹配您的代码:

let option = Some("hello");

match option.unwrap_or(&format!("unhandled string").as_str()) {
    "hello" => {
        println!("hello");
    }
    _ => {
        println!("unknown string");
    }
};