본문 바로가기
Languages/RUST

[RUST] TRPL : Chapter15 - Smart Pointer

by odaebum 2024. 12. 6.
728x90

Chapter 15 - Smart Pointer

  • 포인터 : 메모리에 주소를 포함하는 변수에 대한 일반적인 개념. (참조, 가리킴)
  • 스마트 포인터 : 포인터처럼 작동하지만 추가 메타데이터와 기능도 있는 데이터 구조.
    • 자신이 가리키는 데이터를 소유한다.
    • 특성:
      • Deref : 스마트 포인터 구조체의 인스턴스가 참조처럼 동작하여, 참조 또는 스마트 포인터로 작업하도록 코드 작성 가능
      • Drop : 스마트 포인터의 인스턴스가 범위를 벗어날 때 실행되는 코드를 사용자 지정할 수 있다.
    • 일반적으로 구조체를 사용하여 구현

Box

Box<T> :

  • 스택에 남는 것은 힙 데이터에 대한 포인터이다.
  • 사용 :
    • 컴파일 시점에 크기를 알 수 없는 유형이 있고 정확한 크기가 필요한 컨텍스트에서 해당 유형의 값을 사용
    • 대량의 데이터가 있고 소유권을 이전하려고 하지만 이전 시 데이터가 복사되지 않도록 보장
    • 특정 유형이 아닌 특정 특성을 구현하는 유형인지만 중요시하고 값을 소유하고자 하는 경우
fn main() {
    let b = Box::new(5);
    println!("b = {b}");
}

상자를 활용한 재귀적 유형

  • 재귀는 이론적으로 무한히 계속될 수 있으므로 Rust는 값에 필요한 공간을 알 수 없다.
  • 상자는 알려진 크기를 가지므로 재귀적 유형 정의에 상자를 삽입하여 재귀적 유형을 활성화할 수 있다.
  • 링크드 리스트
  • enum List { Cons(i32, List), //현재 항목과 다음 항목 Nil, //다음 항목 없이 호출된 값만 포함 }

Deref

  • 역참조 연산자\
    • Trait구현을 통한 참조처리
    • struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } use std::ops::Deref; impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 //무한하지 않게함 } } fn main() { let x = 5; let y =MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); }

Deref 강제 변환

Deref Mutability

  1. From &T to &U when T : Deref<Target=U>
  2. From &mut T to &mut U when T : DerefMut<Target=U>
  3. From &mut T to &U when T: Deref<Target=U>

Drop

  • 모든 유형에서 특성에 대한 구현을 제공할 수 있으며, 해당 코드는 파일이나 네트워크 연결과 같은 리소스를 해제하는 데 사용할 수 있다.
  • Box가 드롭되면 상자가 가리키는 힙의 공간을 할당 해제한다.

값 조기 정리

std::mem::drop 을 사용하면 객체를 조기 정리할 수 있다.

  • Rust는 범위를 활용하여 자동으로 메모리 해제를 진행하지만 해당 메서드를 통해 수동으로 진행할 수 있다.

Rc

→ 참조 카운트 스마트 포인터

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
    let b = Cons(3, Box::new(a));
    let c = Cons(4, Box::new(a)); //해당 코드에서는 a의 소유권이 b로 넘어갔기 때문에
}                                 //c에서 받을 a의 소유권이 없어서 에러가 발생함

-------------------

enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use crate::List::{Cons, Nil};
use std::rc::Rc;

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a)); //그러나 Rc를 사용한 clone에서는 복사이기때문에 가능
}
  • Rc::clone은 깊은 복사가 아니기 때문이다.
  • Rc::clone은 참조 카운트를 늘린다.

RefCell

  • 내부 가변성 패턴은 Rust의 디자인 패턴이다.
  • Rc와 다르게 단일 소유권을 보유한다.
  • 주로 멀티 스레드에서 사용함.

Box VS Rc VS RefCell

  • Rc : 하나의 데이타에 다중 소유권이 가능하다; 그 외 불가능.
  • Box : 가변, 불변 참조 모두 컴파일 가능하다.; Rc 불변참조, RefCell 가변 불변을 런타임에서 확인한다.
  • RefCell : 불변일때도 런타임에서는 가변할 수 있게한다. (내부 가변적)
  • Rc::downgrade → Weak
    • weak_count는 0일 필요 없어 삭제될 수 있다.
728x90