'AsRef'의 문제는 무엇입니까?

AsRef 특성은 std lib의 모든 곳에 있으며 매우 편리합니다. 그러나 그것을 사용하는 이점은 너무 분명하지 않을 수 있습니다. 그러나 계속 읽으면 몇 가지 유용한 예와 적용할 위치를 볼 수 있습니다.

실용적인 실제 프로젝트에서 떼어낸 아주 작은 녹 조각을 보여주는 시리즈"practical rust bites"의 두 번째 게시물입니다. 매우 실용적인 예를 통해 한 번에 하나의 Rust 관용구 또는 개념만 설명하는 것을 목표로 합니다.

배경


AsReffound in the std::convert 모듈이 될 수 있으며 사용됩니다.
저렴한 참조 대 참조 변환을 위해.

이것은 std 라이브러리에서 널리 사용되는 매우 필수적인 특성이며 한 유형에서 다른 유형으로 원활한 참조 변환을 돕습니다.
종종 그것은 작동 중이며 당신은 그것이 거기에 있다는 것을 깨닫지 못합니다.

예: 문자열 또는 &str



문자열을 매개변수로 받아들이고 싶었던 함수가 있었나요?
그런 다음 문자열 참조( &str 에서와 같이) 또는 소유 문자열( String에서와 같이)을 수락해야 하는지 스스로에게 질문했을 수도 있습니다.

따라서 다음과 같은 것을 염두에 두어야 합니다.

fn take_a_str(some: &str) {}


그리고 다른 하나:

fn take_a_string(some: String) {}


So why not have both?!



바로 이 시점에서 AsRef<str>가 매우 유용합니다.
str String 유형 모두 이 특성을 구현하기 때문입니다.

fn take_a_str(some: impl AsRef<str>) {
    let some = some.as_ref();
    println!("{some}");
}

fn main() {
    take_a_str("str");
    take_a_str("String".to_string());

    // also `&String` is supported:
    let string_ref = "StringRef".to_string();
    take_a_str(&string_ref);
}


check out the code on the playground

보시다시피 take_a_str 의 이 버전은 AsRef<str> 를 구현하는 유형을 허용합니다.

그렇게 함으로써some: impl AsRef<str> 제공된 유형에 대한 구체적인 가정을 하지 않습니다.
대신 주어진 유형이 이 특성만 구현한다고 주장합니다.

예: 래퍼 유형



이 예제에서는 임의의 구조체에 대해 직접 구현AsRef할 수 있는 방법에 초점을 맞추고자 합니다.

이 작은 프로그램을 살펴보겠습니다.

pub struct Envelope {
    letter: String
}

fn main() {
    let a_letter = Envelope { 
        letter: "a poem".to_string() 
    };

    println!("this is a letter: {}", &a_letter);
}


그러나 물론 지금은 구현하지 않으며Display Rust 컴파일러도 동의한다고 말할 수 있습니다.

error[E0277]: `Envelope` doesn't implement `std::fmt::Display`
  --> src/main.rs:10:38
   |
10 |     println!("this is a letter: {}", &a_letter);
   |                                      ^^^^^^^^^ `Envelope` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Envelope`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.


그러나 지금은 Display를 무시하고 다른 사용 사례에 집중합니다. 즉 Envelope 의 내부 값에 액세스하려고 합니다.

구현AsRef<str>으로 이 문제를 해결하려면 다음을 수행합니다.

impl AsRef<str> for Envelope {
    fn as_ref(&self) -> &str {
        // here we up-call to the `AsRef<str>` implementation for String
        self.letter.as_ref()
    }
}


이것으로 우리는 약간의 조정이 필요합니다.

fn main() {
    let a_letter = Envelope {
        letter: "a poem".to_string()
    };

    println!("this is a letter: {}", a_letter.as_ref());
}


이제 Rust 컴파일러는 더 이상 불평하지 않습니다.

다시 말하지만, 물론 바로 이 Display 사례에 대해 println!를 구현하는 것이 적절할 것입니다.

check out the full example on the playground

예: 구성된 유형



나는 인정해야 합니다. 이 예를 약간의 소금으로 받아들입니다. 이런 용도일 가능성이 매우 높음
더 큰 구조체에 문제가 발생할 수 있습니다. 따라서 일반적으로 구조체에 2개 이상의 필드가 있는 경우 이 경로를 따르지 않는 것이 좋습니다.

가중치를 나타내는 간단한 구조체를 살펴보겠습니다.

struct Weight {
    weight: f32,
    unit: String
}

impl Weight {
    /// Weight in Tons that is 157.47 stones 
    pub fn from_tons(weight: f32) -> Self {
        Self { weight, unit: "t".to_string() }
    }

    /// Weight in Stones
    pub fn from_stones(weight: f32) -> Self {
        Self { weight, unit: "st".to_string() }
    }
}


보시다시피 우리는 pub 필드와 getter 접근자 함수를 제공하지 않았습니다.

So how we can actually get our hand on the data inside?



짐작하셨겠지만, AsRef는 여기서도 우리 친구입니다.

impl AsRef<str> for Weight {
    fn as_ref(&self) -> &str {
        &self.unit
    }
}

impl AsRef<f32> for Weight {
    fn as_ref(&self) -> &f32 {
        &self.weight
    }
}


그래서 여기서 우리는 AsReff32 유형에 대해 str 특성을 사용하여 구조체 내부의 가중치 및 단위에 액세스합니다.

fn main() {
    let a_ton = Weight::from_tons(1.3);

    let tons: &f32 = a_ton.as_ref();
    let unit: &str = a_ton.as_ref();

    println!("a weight of {tons} {unit}");
}


변수 바인딩을 건너뛰고 덜 장황하게 만들 수도 있습니다.

fn main() {
    let a_ton = Weight::from_tons(1.3);

    println!(
        "a weight of {} {}",
        a_ton.as_ref() as &f32,
        a_ton.as_ref() as &str
    );
}


checkout the full example on the playground

마무리


AsRef는 매우 편리한 도구이며 표준 라이브러리를 살펴보면 많은 유형에 대해 구현된 것을 찾을 수 있습니다.
이로 인해 많은 참조 유형이 상호 교환 가능해지고 전반적인 인체 공학이 크게 향상됩니다.

또한 AsRef가 액세서리 메서드나 공용 필드를 제공하지 않고 구조체의 내부 데이터에 액세스하는 데 유용할 수 있는 방법을 살펴보았습니다.

구조체의 내부 값을 가져오는 매우 관련된 함수이지만 값(참조가 아님)은 .into_inner() 입니다.
보시다시피 std lib에서 많이 사용되지만 특성에 구속되지는 않습니다. 따라서 이것은 관례에 가깝습니다. 구조체를 소비하고 내부 래핑된 유형을 펼쳐야 할 때마다 .into_inner()가 공통 스키마입니다.

이 게시물에 사용된 버전:

$ cargo --version && rustc --version
cargo 1.61.0 (a028ae42f 2022-04-29)
rustc 1.61.0 (fe5b13d68 2022-05-18)


새 블로그 게시물 및 기타 녹 관련 뉴스에 대한 업데이트를 받으려면 다음을 수행하십시오.

좋은 웹페이지 즐겨찾기