我经常从计算中获得Option<String>
,我想使用此值或默认的硬编码值。
这对于一个整数来说是微不足道的:
let opt: Option<i32> = Some(3);
let value = opt.unwrap_or(0); // 0 being the default
但是使用String
和&str
,编译器会抱怨类型不匹配:
let opt: Option<String> = Some("some value".to_owned());
let value = opt.unwrap_or("default string");
这里的确切错误是:
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`
一种选择是将字符串切片转换为拥有的String,如rustc:
所示let value = opt.unwrap_or("default string".to_string());
但是这会导致分配,当我想立即将结果转换回字符串切片时,这是不合需要的,就像调用Regex::new()
一样:
let rx: Regex = Regex::new(&opt.unwrap_or("default string".to_string()));
我希望将Option<String>
转换为Option<&str>
以避免此分配。
写这个的人是什么?
答案 0 :(得分:34)
您可以使用as_ref()
和map()
将Option<String>
转换为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");
}
首先,as_ref()
隐含地在opt
上引用,提供&Option<String>
(因为as_ref()
接受&self
,即它接收引用),以及把它变成Option<&String>
。然后我们使用map
将其转换为Option<&str>
。这是&**x
的作用:最右边的*
(首先评估)只是取消引用&String
,给出String
左值。然后,最左边的*
实际调用Deref
特征,因为String
implements Deref<Target=str>
,给我们一个str
左值。最后,&
获取str
左值的地址,为我们提供&str
。
您可以使用map_or
在单个操作中合并map
和unwrap_or
来进一步简化此操作:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", |x| &**x);
}
如果&**x
看起来太神奇了,您可以改为编写String::as_str
:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_str);
}
或String::as_ref
(来自AsRef
中的prelude特征):
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_ref);
}
或String::deref
(虽然您也需要导入Deref
特征):
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);
}
要使其中任何一个生效,只要Option<String>
或未展开的Option<&str>
需要保持可用,您就需要保留&str
的所有者。如果这太复杂了,您可以使用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));
}
答案 1 :(得分:18)
一种更好的方法可能是T: Deref
:
use std::ops::Deref;
trait OptionDeref<T: Deref> {
fn as_deref(&self) -> Option<&T::Target>;
}
impl<T: Deref> OptionDeref<T> for Option<T> {
fn as_deref(&self) -> Option<&T::Target> {
self.as_ref().map(Deref::deref)
}
}
有效地概括了as_ref
。
答案 2 :(得分:9)
标准库具有不稳定的仅夜间功能Option::deref
可以做到这一点:
var $grid = $('.grid').masonry({
itemSelector: '.grid-item',
percentPosition: true,
columnWidth: '.grid-sizer'
});
$grid.imagesLoaded().progress( function() {
$grid.masonry('layout');
});
答案 3 :(得分:3)
虽然我喜欢Veedrac的答案(我使用过它),但如果您只需要一点就想要表达某些东西,那么您可以使用as_ref()
,map
和{ {1}}链:
String::as_str
答案 4 :(得分:2)
这是你可以做到的一种方式。请注意, 要保留原始String
,否则&str
会成为什么?
let opt = Some(String::from("test")); // kept around
let unwrapped: &str = match opt.as_ref() {
Some(s) => s, // deref coercion
None => "default",
};