Rc<T>
대부분의 상황에서 어떤 값에 대한 소유권이 어떤 변수에 있는지를 정확하게 알 수 있습니다. 러스트의 소유권 규칙은 하나의 값에 단 하나만의 소유자를 보장합니다. 그런데 만일 하나의 값에 여러 개의 소유자를 정말로 가지고 싶다면 어떻게 할까요? 이럴 때 사용할 수 있는 자료형이 바로 Rc<T>
입니다. 레퍼런스 카운팅(Reference counting)의 앞 자를 따서 만든 이름으로, Rc<T>
역시 스마트 포인터입니다.
use std::rc::Rc; fn main() { let origin = Rc::new(1); assert_eq!(1, *origin); }
마지막 순간까지
프로그램의 여러 부분이 읽을 수 있도록 힙에 일부 데이터를 할당하고 컴파일 시점에 어느 부분이 데이터를 마지막으로 사용할지 결정할 수 없을 때 Rc<T>
타입을 사용합니다. 어떤 부분이 마지막으로 완료될지 알 수 있다면 해당 부분을 데이터의 소유자로 설정하면 컴파일 시점에 적용되는 일반적인 소유권 규칙이 적용될 것입니다.
일반적인 레퍼런스와 Rc<T>
가 다른 점은 여기에 있습니다. 다음과 같은 예제를 살펴보겠습니다.
fn main() { let cloned; { let origin = "Rust".to_string(); cloned = &origin; // 🤯 } println!("{}", cloned); }
하지만 Rc
를 사용하면 원래 값이 스코프를 벗어나더라도 값을 참조하고 있는 Rc
가 존재하기 때문에 여전히 값을 사용할 수 있습니다. 여기서 clone
을 사용하면, 실제로 값이 복사되는 것이 아니라 Rc
의 레퍼런스 카운트가 1 증가합니다.
use std::rc::Rc; fn main() { let cloned; { let origin = Rc::new(1); cloned = origin.clone(); } println!("{}", cloned); }
레퍼런스 카운팅
Rc<T>
는 값의 소유권을 가지고 있는 변수가 몇 개인지를 계속 확인하고 있다가, 값을 소유하고 있는 변수가 전부 사라지면 값을 메모리에서 삭제합니다.
use std::rc::Rc; fn main() { let origin = Rc::new(0); println!("Reference count: {}", Rc::strong_count(&origin)); { let _dup1 = Rc::clone(&origin); println!("Reference count: {}", Rc::strong_count(&origin)); { let _dup2 = &origin.clone(); println!("Reference count: {}", Rc::strong_count(&origin)); } println!("Reference count: {}", Rc::strong_count(&origin)); } println!("Reference count: {}", Rc::strong_count(&origin)); // origin drops here }
실행 결과
Reference count: 1
Reference count: 2
Reference count: 3
Reference count: 2
Reference count: 1
Rc<T>
는 멀티스레드 환경에서 동작하지 않습니다. 멀티스레드 환경에서는Arc<T>
를 사용해야 하며, 자세한 내용은 나중에 다루겠습니다.
Quiz
struct Node { value: i32, next: Option<Box<Node>>, } fn main() { let mut head1 = Node { value: 1, next: None, }; let node1 = Node { value: 2, next: None, }; head1.next = Some(Box::new(node1)); let mut head2 = Node { value: 3, next: None, }; head2.next = Some(Box::new(node1)); // 🤯 println!("{} {}", head1.value, head1.next.unwrap().value); println!("{} {}", head2.value, head2.next.unwrap().value); }
정답
use std::rc::Rc; struct Node { value: i32, next: Option<Rc<Node>>, } fn main() { let mut head1 = Node { value: 1, next: None, }; let node1 = Rc::new(Node { value: 2, next: None, }); head1.next = Some(Rc::clone(&node1)); let mut head2 = Node { value: 3, next: None, }; head2.next = Some(Rc::clone(&node1)); println!("{} {}", head1.value, head1.next.unwrap().value); println!("{} {}", head2.value, head2.next.unwrap().value); }