Option <String>에서 Option <& str>로 변환


85

매우 자주 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 방법은 무엇입니까?

답변:


66

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

그것은 나를 위해 작동하지만 사용하는 다른 버전 map이 작동하지 않는 이유를 이해할 수 없습니다 . 코드 변형의 Options<String>경우 첫 번째 와이를 참조하는 사본에 어떤 일이 발생하는지 이해하지 못합니다 as_deref. 다음을 사용하는 작업 코드는 다음과 같습니다 as_deref.let device_id = UsbDeviceIdentifier::VidPidSn { vid: device.vendor_id, pid: device.product_id, sn: device.serial_number.as_deref().unwrap_or("") }; 내 첫 번째 시도는 let device_id = UsbDeviceIdentifier::VidPidSn { vid: device.vendor_id, pid: device.product_id, sn: device.serial_number.map(|s| s.as_str()).unwrap_or("") };.
Paul-Sebastian Manole

내 오류는 error[E0515]: cannot return value referencing function parameter `s`, s.as_str() returns a value referencing data owned by the current function입니다. OK,하지만 이유는 무엇입니까 as_deref여전히 원래의 참조 생성하기 때문에, 작업 Option<String>에 의해 반환을 .serial_number데이터가 아직 포인트가 소유 그래서 Option!? 차이점은as_deref반환하는 슬라이스의 소스를 버리는 클로저의 본문에서 수행하는 것과 반대로 변형 수행하는 있습니까? 그렇다면 그것을 고칠 방법이 있습니까? str의 복사본을 반환하는 것처럼?
Paul-Sebastian Manole

@ Paul-SebastianManole 읽어주세요 사용 방법을 보여주는 기존 답변을 십시오map . 문제가 해결 되나요?
Shepmaster

그래,하지만 여전히 어리둥절 해.
Paul-Sebastian Manole


55

당신은 사용할 수 있습니다 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));
}

13
bikesheddy 코멘트 : 대신 map(|x| &**x)당신도 할 수 있습니다 map(String::as_ref).
fjh

2
약간 장황하지만 잘 작동합니다. 의 유형을 명확히하기 위해 opt.as_ref()IS를 Option<&String>, 다음 opt.as_ref().map(String::as_ref)이 전달 &String하는 String::as_ref를 반환하는 &str. &String에서 Option::as_ref으로 강제 할 수없는 이유는 무엇 &str입니까?
George Hilliard 2015

1
@thirtythreeforty가 String::as_ref를 반환 &str아니라 &&String(참조 여기에 )
fjh

@fjh 방금 그것을 깨달았고 편집했습니다 :). 내 강제 질문이 남아 있습니다.
George Hilliard 2015

1
@ 리틀 친구 :에서가 &**, (가) 왼쪽 *실제로 호출 Deref하기 때문에, 특성을 String구현합니다 Deref<Target=str>. Deref::derefAsRef::as_ref모두 레퍼런스로 참조 전환을 제공하고,로 변환하는 것이 발생 &String하는 것이 &str모두 가능하다. map(String::deref)대신 사용할 수 map(String::as_ref)있으며 동등합니다.
Francis Gagné

20

더 좋은 방법은 이것을 일반적으로 구현하는 것입니다 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합니다.


1
이것은 훌륭한 유틸리티 기능입니다. Option <T>의 함수로서 표준 라이브러리의 일부 였으면 좋겠습니다!
Bill Fraser

1
감사! 이것은 나를 위해 작동합니다.이 코드를 포함하면 opt.as_deref()실제로 Option<&str>.
rspeer jul.

7

나는 (내가 그것을 사용) 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));

1

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

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

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

놀이 틀


2
그것은 그것을 Option <& str>로 바꾸지 않았습니다.
rspeer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.