스마트 포인터: 단일 소유권을 넘어선 유연한 설계
학습 목표: Rust의 단일 소유권 원칙이 한계에 부딪힐 때 사용하는 스마트 포인터들을 마스터합니다. 힙 할당(
Box), 공유 소유권(Rc/Arc), 내부 가변성(RefCell), 그리고 효율적인 리소스 관리를 위한Cow와Drop트레이트를 배웁니다.
1. Box<T>: 단순한 힙 할당
C#의 모든 클래스 인스턴스는 힙에 저장되지만, Rust는 스택이 기본입니다. Box<T>를 사용하여 명시적으로 데이터를 힙으로 보낼 수 있습니다. 주로 재귀적 데이터 구조나 트레이트 객체를 만들 때 사용합니다.
#![allow(unused)] fn main() { // [C# 스타일의 객체 생성과 유사] let b = Box::new(42); // 42라는 값을 힙에 저장하고 포인터를 스택에 둠 }
2. Rc<T>와 Arc<T>: 공유 소유권
데이터를 여러 곳에서 동시에 소유해야 할 때(예: 그래프 구조, 공유 설정값) 사용합니다.
Rc<T>: 단일 스레드용. 참조 횟수를 기록하여 마지막 주인이 사라질 때 메모리를 해제합니다.Arc<T>: 멀티 스레드용. 원자적(Atomic) 연산을 사용하여 여러 스레드 간에 안전하게 공유합니다.
3. 내부 가변성: RefCell<T>와 Mutex<T>
불변 참조(&self) 뒤에 있는 데이터를 수정해야 할 때 쓰는 '치트키'입니다.
RefCell<T>: 런타임에 빌림 검사를 수행합니다. 단일 스레드에서 유용합니다.Mutex<T>/RwLock<T>: 스레드 안전한 수정을 가능하게 합니다.
#![allow(unused)] fn main() { // [불변 참조임에도 데이터를 수정하는 예시] let logger = Logger { entries: RefCell::new(vec![]) }; logger.entries.borrow_mut().push("로그 기록".to_string()); }
4. Drop: Rust의 IDisposable
C#의 using 블록이나 Dispose() 호출을 잊어버려 리소스가 새는 걱정을 하셨나요? Rust는 Drop 트레이트를 통해 값이 스코프를 벗어나는 순간 관련 리소스를 확정적으로 정리합니다.
- RAII 패턴: "리소스 획득은 초기화이며, 수명 종료는 곧 반납이다."
💡 스마트 포인터 선택 가이드
- 단순 힙 할당이 필요하다면? →
Box<T> - 단일 스레드에서 소유권을 공유한다면? →
Rc<T> - 멀티 스레드에서 소유권을 공유한다면? →
Arc<T> - 불변 참조로 데이터를 고쳐야 한다면? →
RefCell<T>(단일) 또는Mutex<T>(멀티) - 읽기 우선이지만 가끔 수정하고 싶다면? →
Cow<'a, T>