我可以直接匹配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!(),
}
虽然有效,但其冗长程度很糟糕。
在这种情况下,我的代码只是一个例子。我无法控制String
或Some(String)
的创建 - 所以我无法在实际中更改此类型,就像我在我的示例中所做的那样。
还有其他选择吗?
答案 0 :(得分:9)
这是Rust的模式的已知限制。
方法调用(包括==
等运算符的内部方法)会根据需要自动调用.deref()
,因此String
会自动转换为&str
,以便与文字进行比较。
OTOH这些模式在比较中非常直观,并且发现String
和&str
不同。
有两种解决方案:
在匹配之前将Option<String>
更改为Option<&str>
:Some(a).as_ref().map(String::as_str)
。 as_ref()
生成Option<&String>
(阻止移动),然后as_str()
明确地将其引用为&str
。
使用比赛后卫:match Some(a) { Some(ref s) if s == "hello" => … }
。 Some(ref s)
与String
匹配,并将其捕获为s: &String
,然后您可以在执行常规灵活强制操作的if
后卫中对其进行比较。
另见:
答案 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)
您发现的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_or
将Option::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");
}
};