728x90
반응형

15장. 스마트 포인터

Rust의 스마트 포인터는 메모리 관리와 자원 관리를 자동화하는 데 사용됩니다. 스마트 포인터는 일반 포인터와 달리 추가적인 메타데이터와 기능을 제공합니다. Rust의 주요 스마트 포인터에는 Box<T>, Rc<T>, RefCell<T> 등이 있습니다.

Box

Box<T>는 힙에 데이터를 저장하고, 스택에는 힙 데이터에 대한 포인터를 저장합니다. 주로 크기가 큰 데이터를 힙에 저장하거나, 재귀적인 데이터 구조를 정의할 때 사용됩니다.

Box 예제

fn main() {
    let b = Box::new(5);
    println!("b = {}", b);
}

위 예제에서 Box::new를 사용하여 힙에 정수 5를 저장하고, b는 힙 데이터에 대한 포인터를 가집니다.

Rc

Rc<T>는 참조 카운팅(reference counting) 스마트 포인터로, 여러 소유자가 동일한 데이터를 가리킬 수 있게 합니다. 주로 단일 스레드 환경에서 사용됩니다.

Rc 예제

use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);

    println!("a = {}, b = {}, c = {}", a, b, c);
    println!("Reference count: {}", Rc::strong_count(&a));
}

위 예제에서 Rc::new를 사용하여 정수 5를 참조 카운팅 스마트 포인터로 감싸고, Rc::clone을 사용하여 여러 소유자가 동일한 데이터를 가리키게 합니다. Rc::strong_count를 사용하여 참조 카운트를 확인할 수 있습니다.

RefCell

RefCell<T>는 런타임에 가변성을 제공하는 스마트 포인터로, 불변 참조와 가변 참조를 런타임에 검사합니다. 주로 단일 스레드 환경에서 내부 가변성을 허용할 때 사용됩니다.

RefCell 예제

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(5);

    {
        let mut y = x.borrow_mut();
        *y += 1;
    }

    println!("x = {}", x.borrow());
}

위 예제에서 RefCell::new를 사용하여 정수 5를 감싸고, borrow_mut을 사용하여 가변 참조를 얻어 값을 변경합니다. borrow를 사용하여 불변 참조를 얻어 값을 읽습니다.

스마트 포인터의 조합

스마트 포인터를 조합하여 복잡한 데이터 구조를 만들 수 있습니다. 예를 들어, Rc<T>RefCell<T>를 조합하여 여러 소유자가 가변 데이터를 공유할 수 있습니다.

Rc와 RefCell 조합 예제

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    next: Option<Rc<RefCell<Node>>>,
}

fn main() {
    let node1 = Rc::new(RefCell::new(Node { value: 1, next: None }));
    let node2 = Rc::new(RefCell::new(Node { value: 2, next: None }));

    node1.borrow_mut().next = Some(Rc::clone(&node2));
    node2.borrow_mut().next = Some(Rc::clone(&node1));

    println!("Node 1: {:?}", node1);
    println!("Node 2: {:?}", node2);
}

위 예제에서 Node 구조체는 Rc<RefCell<Node>>를 사용하여 가변 데이터를 여러 소유자가 공유할 수 있게 합니다. node1node2는 서로를 가리키는 순환 참조를 가집니다.

Summary

  • Box

    • 힙 메모리에 데이터를 저장하는 가장 단순한 스마트 포인터
    • 재귀적 타입과 컴파일 타임에 크기를 알 수 없는 타입을 다룰 때 유용
    • 예제: 연결 리스트 구현하기
      enum List {
          Cons(i32, Box<List>),
          Nil,
      }
  • Deref 트레잇

    • 참조 역참조 연산자(*) 동작을 커스터마이징
    • 스마트 포인터를 일반 참조자처럼 다룰 수 있게 해줌
    • 예제: 커스텀 Box 타입 구현하기
      struct MyBox<T>(T);
      impl<T> Deref for MyBox<T> {
          type Target = T;
          fn deref(&self) -> &T {
              &self.0
          }
      }
  • Drop 트레잇

    • 값이 스코프를 벗어날 때 실행될 코드를 지정
    • 파일이나 네트워크 연결 등의 리소스 정리에 사용
    • 예제: 커스텀 스마트 포인터의 정리 코드
      struct CustomSmartPointer {
          data: String,
      }
      impl Drop for CustomSmartPointer {
          fn drop(&mut self) {
              println!("Dropping CustomSmartPointer with data `{}`!", self.data);
          }
      }
  • Rc

    • Reference Counting 스마트 포인터
    • 데이터를 여러 소유자가 공유할 수 있게 해줌
    • 예제: 여러 리스트가 데이터 공유하기
      use std::rc::Rc;
      let a = Rc::new(List::Cons(5, Rc::new(List::Nil)));
      let b = List::Cons(3, Rc::clone(&a));
      let c = List::Cons(4, Rc::clone(&a));
  • RefCell와 내부 가변성

    • 불변 참조자를 통해 가변 데이터에 접근하는 패턴
    • 런타임에 빌림 규칙을 검사
    • 예제: 가변 상태를 가진 불변 객체 만들기
      use std::cell::RefCell;
      struct MockMessenger {
          sent_messages: RefCell<Vec<String>>,
      }
      impl MockMessenger {
          fn send(&self, message: &str) {
              self.sent_messages.borrow_mut().push(String::from(message));
          }
      }

참조: https://doc.rust-lang.org/book/ch15-00-smart-pointers.html

728x90
반응형

'IT Best Practise > Rust' 카테고리의 다른 글

17. 객체지향 프로그래밍  (0) 2024.11.07
16. 동시성  (0) 2024.11.07
14. Cargo와 Crates.io  (1) 2024.11.07
13. 함수형 프로그래밍  (0) 2024.11.07
12. I/O 프로젝트  (0) 2024.11.07

+ Recent posts