Rust에는 전통적인 가비지 컬렉터가 없기 때문에 복잡한 구조체에 대한 소유권 관리를 위해 스마트 포인터(smart pointers)를 사용합니다. 가장 자주 사용되는 포인터는 다음과 같습니다:
Box<T> — 객체를 힙에 할당하고 그에 대한 소유권을 전달합니다. 컴파일 시 데이터 크기가 불확실하거나 이동할 수 있지만 고유한 자원이 필요할 때 사용됩니다.
Rc<T> (Reference Counted) — 참조 카운트를 제공하여 여러 변수가 불변 데이터를 "공유"하는 소유권을 허용합니다 (단일 스레드 컨텍스트에서만).
Arc<T> (Atomic Reference Counted) — 또한 참조 카운트를 구현하지만 원자적입니다; 다중 스레드 프로그램에서 사용이 허용됩니다.
RefCell<T> — 실행 시 "내부 가변" 소유권을 제공하며, 불변 참조를 통해서도 콘텐츠를 변경할 수 있지만 단일 스레드에서만 가능 (스레드 안전하지 않습니다!).
예:
use std::rc::Rc; let a = Rc::new(vec![1,2,3]); let b = Rc::clone(&a); // 이제 a와 b는 같은 데이터의 소유자입니다
모든 스레드가 데이터만 읽는 경우 Rc<T>를 다중 스레드 코드에서 사용할 수 있나요? 설명하세요.
답변: 아니요, 안 됩니다! Rc<T>가 데이터에 대한 불변 접근만 허용하더라도, Rc<T> 컨테이너 자체는 스레드 안전하지 않기 때문에 내부 참조 카운트가 데이터 경합으로부터 보호되지 않습니다. 이를 위해 Arc<T>가 필요합니다 — 내부 카운터가 스레드 안전합니다.
예:
// 다음 코드는 컴파일되지 않습니다! use std::thread; use std::rc::Rc; let five = Rc::new(5); for _ in 0..10 { let five = Rc::clone(&five); thread::spawn(move || { println!("{}", five); }); }
이야기
이야기
이야기
비즈니스 로직 모듈에서는 RefCell<T>를 사용하여 Arc<T>를 통해 스레드 간에 전달되는 데이터에 대한 가변 접근을 조직했습니다. 그러나 RefCell<T>와 Arc<T>를 결합하려다 데이터 경합과 실행 중 패닉이 발생했습니다. 스레드 안전한 옵션으로는 RefCell<T> 대신 Mutex<T> 또는 RwLock<T>를 사용하는 것이 좋습니다.