Very often I have obtained an Option<String> from a calculation, and I would like to either use this value or a default hardcoded value.
This would be trivial with an integer:
let opt: Option<i32> = Some(3);
let value = opt.unwrap_or(0); // 0 being the default
But with a String and a &str, the compiler complains about mismatched types:
let opt: Option<String> = Some("some value".to_owned());
let value = opt.unwrap_or("default string");
The exact error here is:
error[E0308]: mismatched types
--> src/main.rs:4:31
|
4 | let value = opt.unwrap_or("default string");
| ^^^^^^^^^^^^^^^^
| |
| expected struct `std::string::String`, found reference
| help: try using a conversion method: `"default string".to_string()`
|
= note: expected type `std::string::String`
found type `&'static str`
One option is to convert the string slice into an owned String, as suggested by rustc:
let value = opt.unwrap_or("default string".to_string());
But this causes an allocation, which is undesirable when I want to immediately convert the result back to a string slice, as in this call to Regex::new():
let rx: Regex = Regex::new(&opt.unwrap_or("default string".to_string()));
I would rather convert the Option<String> to an Option<&str> to avoid this allocation.
What is the idiomatic way to write this?
As of Rust 1.40, the standard library has Option::as_deref to do this:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_deref().unwrap_or("default string");
}
See also:
You can use as_ref() and map() to transform an Option<String> into an Option<&str>.
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map(|x| &**x).unwrap_or("default string");
}
First, as_ref() implicitly takes a reference on opt, giving an &Option<String> (because as_ref() takes &self, i.e. it receives a reference), and turns it into an Option<&String>. Then we use map to convert it to an Option<&str>. Here's what &**x does: the rightmost * (which is evaluated first) simply dereferences the &String, giving a String lvalue. Then, the leftmost * actually invokes the Deref trait, because String implements Deref<Target=str>, giving us a str lvalue. Finally, the & takes the address of the str lvalue, giving us a &str.
You can simplify this a bit further by using map_or to combine map and unwrap_or in a single operation:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", |x| &**x);
}
If &**x looks too magical to you, you can write String::as_str instead:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_str);
}
or String::as_ref (from the AsRef trait, which is in the prelude):
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_ref);
}
or String::deref (though you need to import the Deref trait too):
use std::ops::Deref;
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::deref);
}
For either of these to work, you need to keep an owner for the Option<String> as long as the Option<&str> or unwrapped &str needs to remain available. If that's too complicated, you could use Cow.
use std::borrow::Cow::{Borrowed, Owned};
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.map_or(Borrowed("default string"), |x| Owned(x));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With