[rust] Option <String>에서 Option <& str>로 변환

매우 자주 Option<String>계산 에서을 얻었 으며이 값이나 기본 하드 코딩 된 값을 사용하고 싶습니다.

이것은 정수로 간단합니다.

let opt: Option<i32> = Some(3);
let value = opt.unwrap_or(0); // 0 being the default

그러나 a String및 a &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`

한 가지 옵션은 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>.

이것을 작성하는 idomatic 방법은 무엇입니까?



답변

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


답변

당신은 사용할 수 있습니다 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. 맨 오른쪽 *(먼저 평가됨)은 단순히를 역 참조하여 lvalue를 &String제공합니다 String. 그런 다음 가장 왼쪽은 *실제로 Deref특성을 호출합니다 .을 String 구현 Deref<Target=str> 하여 strlvalue를 제공하기 때문 입니다. 마지막으로 &strlvalue 의 주소를 가져 와서 &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 에있는 특성에서 ) :

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(), map그리고 String::as_str체인 :

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

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


답변

할 수있는 한 가지 방법이 있습니다. 당신이 있다는 사실을 숙지 원래 유지하기 위해 String주변을, 그렇지 않으면 어떤이는 것, &str로 조각을 할 수?

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

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

놀이 틀


답변