ABOUT ME

-

Total
-
  • Rust: AsMut, AsRef, Deref, DerefMut 정리
    컴퓨터/Rust 2022. 12. 31. 21:22
    728x90
    반응형

    Intro

    Rust의 AsRef, AsMut, Deref 및 DerefMut trait은 값을 변환하고 참조로 사용하는 방법을 제공한다.

    AsRef 및 AsMut trait을 사용하면 한 type의 값에 대한 참조를 다른 유형의 값에 대한 불변 또는 변경 가능한 참조로 변환할 수 있고, Deref와 DerefMut trait을 사용하면 값을 각각 참조 또는 변경 가능한 참조인 것처럼 사용할 수 있다.

     

    AsRef

    AsRef을 사용하면 참조를 immutable 참조로 변환할 수 있다.

     

    AsRef example1

    이 예제는 i32 value이라는 단일 필드를 가진 MyStruct라는 구조를 정의하고, MyStruct에 대한 AsRef<i32> trait을 구현하여 MyStruct.value에 대한 참조를 i32 값에 대한 immutable 참조로 변환할 수 있게 하였다.

    MyStruct에 대한 AsRef<i32> trait 구현은 단순히 MyStruct.value 필드에 대한 참조를 반환한다.

    또한 MyStruct에 대한 AsRef<str> trait을 구현하는데, 이를 통해 MyStruct.value에 대한 참조를 str 값에 대한 immutable 참조로 변환할 수 있다.

    MyStruct에 대한 AsRef<str> trait 구현은 MyStruct.value 필드에서 문자열 값을 생성하고 이에 대한 참조를 반환한다.

    use std::convert::AsRef;
    
    #[derive(Debug)]
    struct MyStruct {
        value: i32,
        s: String,
    }
    
    // Implement AsRef<i32> for MyStruct
    impl AsRef<i32> for MyStruct {
        fn as_ref(&self) -> &i32 {
            &self.value
        }
    }
    
    // Implement AsRef<str> for MyStruct
    impl AsRef<str> for MyStruct {
        fn as_ref(&self) -> &str {
            &self.s
        }
    }
    
    fn main() {
        let s = MyStruct { value: 10, s: "10 value".to_string() };
    
        // Use the AsRef trait to convert a reference to a MyStruct value to an immutable reference to an i32 value
        let x: &i32 = s.as_ref();
        println!("{}", *x); // 10 출력
    
        // Use the AsRef trait to convert a reference to a MyStruct value to an immutable reference to a str value
        let y: &str = s.as_ref();
        println!("{}", y); // 10 value 출력
    }

     

    AsMut

    AsMut 를 사용하면 참조를 mutable 참조로 변환할 수 있다.

     

    AsMut example 1

    foo 함수는 AsRef<str>와 디버그 trait을 모두 구현해야 하는 T형 매개변수를 취한다.

    그런 다음 foo 함수는 s (&str 타입)에 as_ref()를  호출한 결과를 출력한다.

    AsRef trait을 사용하면 각각의 경우에 대해 별도의 코드를 작성할 필요 없이 String 및 &str 값과 함께 작동하는 코드를 작성할 수 있다.

    이렇게 하면 코드를 보다 유연하고 쉽게 유지 관리할 수 있다.

    use std::fmt::Debug;
    
    fn foo<T: AsRef<str> + Debug>(s: T) {
        println!("{:?}: {:?}", s, s.as_ref());
    }
    
    fn main() {
        let s = "hello".to_string();
        foo(s);
    }

     

    AsMut / &mut

    mutable 참조 (&mut T)와 AsMut trait을 이용해 참조를 mutable 참조로 (T::as_mut(&mut self) -> &mut Self::Target) 바꾸는 둘의 차이점은 참조되는 값의 type이다.

    mutable 참조(&mut T)를 사용하면 참조할 객체의 값을 수정할 수 있지만 참조 유형은 &mut T로 고정된다.

    즉, 유형 T의 값을 수정하는 데는 mutable 참조만 사용할 수 있다.



    반면 AsMut 특성을 사용하면 모든 유형의 값에 대한 참조를 동일한 값에 대한 변경 가능한 참조로 변환할 수 있다.

    이 기능은 다른 유형의 값에 대한 참조를 사용할 수 있지만 값을 수정해야 하는 코드를 작성하려는 경우에 유용하다.

    use std::convert::AsMut;
    
    #[derive(Debug)]
    struct MyStruct {
        value: i32,
    }
    
    impl AsRef<i32> for MyStruct {
        fn as_ref(&self) -> &i32 {
            &self.value
        }
    }
    
    impl AsMut<i32> for MyStruct {
        fn as_mut(&mut self) -> &mut i32 {
            &mut self.value
        }
    }
    
    fn modify(v: &mut MyStruct) {
        v.value = 3;
    }
    
    fn modify_asmut<T: AsMut<i32> + ?Sized>(v: &mut MyStruct, value: i32) {
        *v.as_mut() = value; // 바로 v.value mutable reference 빌려옴
    }
    
    fn main() {
        let mut s = MyStruct { value: 10 };
        
        let w = &mut s;
        modify(w);
        println!("modify: {:?}", s); // 3
        
        modify_asmut::<dyn AsMut<i32>>(&mut s, 4);
        println!("modify_asmut: {:?}", s); // 4
        
    }

     

    Deref

    pub trait Deref {
        type Target: ?Sized;
    
        fn deref(&self) -> &Self::Target;
    }

    Deref trait 을 사용하면 값을 다른 값에 대한 참조인 것처럼 처리할 수 있다.

    이 기능은 각 경우에 대해 별도의 코드를 작성할 필요 없이 소유한 값과 값에 대한 참조를 모두 사용할 수 있는 코드를 작성하려는 경우에 유용하다.

     

    또 Deref trait 는 Target type alias와 Deref 메서드의 구현을 정의하여 유형에 대해 구현된다.

    Target type 값은 값을 참조하는 것처럼 처리할 type을 지정하고 deref 메서드는 대상 type에 대한 참조를 반환한다.

     

    * ?Sized 매개 변수는 Rust의 특수한 유형 매개 변수로 적용되는 type이 고정된 크기를 가질 수도 있고 가지지 않을 수도 있음을 나타낸다.

     

    Deref example

    이 예에서 MyStruct 구조에는 i32인 단일 필드 값이 있고 Deref trait가 MyStruct용으로 구현되며, i32 타입이 Target 타입이다.

    deref 메서드는 value 필드에 대한 참조를 반환한다.

    그런 다음 * 연산자를 사용하여 MyStruct 유형의 값을 i32 값에 대한 참조인 것처럼 참조 해제할 수 있습니다. 예:

    use std::ops::Deref;
    
    struct MyStruct {
        value: i32,
    }
    
    impl Deref for MyStruct {
        type Target = i32;
    
        fn deref(&self) -> &Self::Target {
            &self.value
        }
    }
    
    fn main() {
        let s = MyStruct { value: 10 };
        let x = &s;
    
        println!("{}", *x);  // prints 10
    }

     

    DerefMut example

    DerefMut trait은 type에 대해 * 연산자를 오버로드하여 type을 변경 가능한 참조인 것처럼 사용할 수 있게 해 준다.

    Deref trait의 하위 집합 (type에 대해 * 연산자를 오버로드하여 해당 유형을 참조인 것처럼 사용할 수 있게 해주는 것)

    use std::ops::{Deref, DerefMut};
    
    struct DerefMutExample<T> {
        value: T
    }
    
    impl<T> Deref for DerefMutExample<T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            &self.value
        }
    }
    
    impl<T> DerefMut for DerefMutExample<T> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.value
        }
    }
    
    let mut x = DerefMutExample { value: 'a' };
    *x = 'b';
    assert_eq!('b', x.value);

     

    AsRef/AsMut/AsRef

    이 예제에서 MyStruct 구조에는 i32인 단일 필드 값이 있고, AsRef 및 AsMut trait은 MyStruct에 대해 구현되며, i32 type이 대상 유형이다.

    Deref trait 또한 MyStruct에 대해 구현되며, 대상 유형은 i32이다.

    main 함수에서는 value 필드가 10으로 설정된 MyStruct 값을 생성하고 s에 대한 참조를 x에 바인딩된 i32 값에 대한 immutable 참조로 변환하기 위해 as_ref()을 사용한다.

    use std::convert::{AsRef, AsMut};
    use std::ops::Deref;
    
    #[derive(Debug)]
    struct MyStruct {
        value: i32,
    }
    
    impl AsRef<i32> for MyStruct {
        fn as_ref(&self) -> &i32 {
            &self.value
        }
    }
    
    impl AsMut<i32> for MyStruct {
        fn as_mut(&mut self) -> &mut i32 {
            &mut self.value
        }
    }
    
    impl Deref for MyStruct {
        type Target = i32; // Associated Type
    
        fn deref(&self) -> &Self::Target {
            &self.value
        }
    }
    
    fn main() {
        let mut s = MyStruct { value: 10 };
    
        // Use the AsRef trait to convert a reference to a MyStruct value to an immutable reference to an i32 value
        let x = s.as_ref();
        println!("{:#?}", *x);
    
        // Use the AsMut trait to convert a reference to a MyStruct value to a mutable reference to an i32 value
        let y = s.as_mut();
        *y = 20;
        println!("{}", s.value);
    
        // Use the Deref trait to treat a MyStruct value as if it were an i32 value
        let z = &s;
        println!("{:#?}", *z);
    }

     

    Summary

    Trait Description
    AsRef 한 type의 값에 대한 참조를 다른 type의 값에 대한 immutable 참조로 변환할 수 있다.
    AsMut 한 type의 값에 대한 참조를 다른 type의 값에 대한 mutable 참조로 변환할 수 있다.
    Deref type에 대한 * 연산자를 오버로드하여 type을 참조인 것처럼 사용할 수 있다.
    DerefMut type에 대한 * 연산자를 오버로드하여 type을 mutable 참조인 것처럼 사용할 수 있다.
    728x90

    '컴퓨터 > Rust' 카테고리의 다른 글

    Rust: 카카오 Karlo API wrapper  (0) 2023.03.24
    Rust: Send, Sync 정리  (0) 2022.12.29
    Rust: Ref, Arc, Rc, Mutex 문법 정리  (0) 2022.12.29

    댓글