从Option <string>转换为Option&lt;&amp; str&gt;

时间:2015-07-05 19:02:38

标签: rust

我经常从计算中获得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>以避免此分配。

写这个的人是什么?

5 个答案:

答案 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在单个操作中合并mapunwrap_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",
};

playpen