I can straight-forwardly match a String
in Rust:
let a = \"hello\".to_string();
match &a[..] {
\"hello\" => {
println!(\"Mat
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.
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");
}
};
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.
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"),
}
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>
to Option<&str>
before matching on it: Some(a).as_ref().map(String::as_str)
. The as_ref()
makes Option<&String>
(preventing move), and as_str()
then unambiguously references it as a &str
.
Use match guard: 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.
See also: