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>>
를 사용하여 가변 데이터를 여러 소유자가 공유할 수 있게 합니다. node1
과 node2
는 서로를 가리키는 순환 참조를 가집니다.
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
'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 |