I can straight-forwardly match a String
in Rust:
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
. 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.
Any other options?
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 onOption<&str>
, but still not onOption<String>
.In the working example, you turned the
std::String
into a&str
by doing&a[..]
. If you want to match on aOption<String>
, you have to do the same thing.One way is to use nested matches:
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 anOption<&str>
and match on this, using themap
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 theOption<String>
into aOption<&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.In some cases, you can use
unwrap_or
to replaceOption::None
with a predefined&str
you don't want to handle in any special way.I used this to handle user inputs:
Or to match your code:
It's a known limitation of Rust's patterns.
Method calls (including internal methods for operators like
==
) automatically call.deref()
as needed, soString
gets automagically turned into&str
for comparisons with literals.OTOH the patterns are quite literal in their comparisons, and find that
String
and&str
are different.There are two solutions:
Change
Option<String>
toOption<&str>
before matching on it:Some(a).as_ref().map(String::as_str)
. Theas_ref()
makesOption<&String>
(preventing move), andas_str()
then unambiguously references it as a&str
.Use match guard:
match Some(a) { Some(ref s) if s == "hello" => … }
.Some(ref s)
matches anyString
, and captures it ass: &String
, which you can then compare in theif
guard which does the usual flexible coercions to make it work.See also: