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 idomatic way to write this?
The standard library has the unstable nightly-only function
Option::deref
to do this:You can use
as_ref()
andmap()
to transform anOption<String>
into anOption<&str>
.First,
as_ref()
implicitly takes a reference onopt
, giving an&Option<String>
(becauseas_ref()
takes&self
, i.e. it receives a reference), and turns it into anOption<&String>
. Then we usemap
to convert it to anOption<&str>
. Here's what&**x
does: the rightmost*
(which is evaluated first) simply dereferences the&String
, giving aString
lvalue. Then, the leftmost*
actually invokes theDeref
trait, becauseString
implementsDeref<Target=str>
, giving us astr
lvalue. Finally, the&
takes the address of thestr
lvalue, giving us a&str
.You can simplify this a bit further by using
map_or
to combinemap
andunwrap_or
in a single operation:If
&**x
looks too magical to you, you can writeString::as_str
instead:or
String::as_ref
(from theAsRef
trait, which is in the prelude):or
String::deref
(though you need to import theDeref
trait too):For either of these to work, you need to keep an owner for the
Option<String>
as long as theOption<&str>
or unwrapped&str
needs to remain available. If that's too complicated, you could useCow
.Although I love Veedrac's answer (I used it), if you need it at just one point and you would like something that is expressive you can use
as_ref()
,map
andString::as_str
chain:A nicer way could be to implement this generically for
T: Deref
:which effectively generalizes
as_ref
.Here's one way you can do it. Keep in mind that you have to keep the original
String
around, otherwise what would the&str
be a slice into?playpen