How can I pattern match against an Option<String>?
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:
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:
- Converting from Option<String> to Option<&str>
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"),
}
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.