Converting from Option<String> to Option<&str>

我经常从计算中得到一个 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");

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`

一种选择是按照 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>来避免这种分配。

写这个的惯用方法是什么?

35494 次浏览

有一个办法。请记住,你 保持原来的 String左右,否则 &str会成为一个切片?

let opt = Some(String::from("test")); // kept around


let unwrapped: &str = match opt.as_ref() {
Some(s) => s, // deref coercion
None => "default",
};

婴儿围栏

可以使用 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");
}

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 opt0, giving a opt1 lvalue. Then, the leftmost * actually invokes the &Option<String>0 trait, because opt1 &Option<String>1 opt5, giving us a opt6 lvalue. Finally, the opt7 takes the address of the opt6 lvalue, giving us a opt9.

通过使用 map_ormapunwrap_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性状,在 前奏中) :

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));
}

一个更好的方法可以是通用地为 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

虽然我喜欢 Veedrac 的回答(我用过) ,但是如果你只需要一个点,并且你想要一些有表现力的东西,你可以使用 as_ref()mapString::as_str链:

let opt: Option<String> = Some("some value".to_string());


assert_eq!(Some("some value"), opt.as_ref().map(String::as_str));

在 Rust 1.40中,标准库使用 Option::as_deref来完成以下工作:

fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_deref().unwrap_or("default string");
}

See also: