[Rust] Rc와 Weak
4. Rc와 Weak
1) Rc
Rc는 Reference counting의 약자입니다.
Reference counting은 참조된 횟수를 카운팅 하는 스마트 포인터입니다.
왜 참조된 횟수를 카운팅할까요?
Rc는 하나의 데이터를 두고 복수의 소유권이 존재하는 것을 허용하는 스마트 포인터입니다.
소유권을 가진 변수가 여러 개다 보니 어느 한 변수의 소유권이 사라질 때 데이터까지 지워 버린다면 소유권을 가지고 있는 다른 변수까지 뜻하지 않게 값을 잃게 됩니다.
이런 일을 방지하기 위해 이 데이터에 대한 소유권을 얼마나 많은 변수들이 가지고 있는지 카운팅을 하는 것입니다.
데이터를 지우는 일은 카운팅이 0이 되어야만 일어나도록 설정되어 있습니다.
이런 방법을 통해 Rc는 한 데이터에 대해 복수의 소유권을 허용할 수 있습니다.
Rc 타입을 사용할 때는 clone을 통해 복수의 소유권을 발생시킬 수 있고 이때 카운팅도 늘어납니다.
코드를 통해서 보겠습니다.
use std::rc::Rc;
fn main(){
let rc = Rc::new(10); // 1 Rc 생성
let mut rc_list = vec![ // 클론을 Vec에 담기
rc.clone(), // 2 Rc 클론
rc.clone(),
rc.clone(),
rc.clone(),
rc.clone()
];
// 클론을 하나씩 지우면서 카운팅 출력
while rc_list.len() > 0 { // 3
println!("strong count: {}", Rc::strong_count(&rc)); // Rc 카운팅
rc_list.pop(); // pop으로 클론 하나씩 삭제
}
println!("strong count: {}", Rc::strong_count(&rc)); // 4
}
// 출력
strong count: 6
strong count: 5
strong count: 4
strong count: 3
strong count: 2
strong count: 1
// 1: Rc::new(10) 메서드를 통해서 10을 값으로 가지고 있는 Rc<i32> 타입 변수 rc를 생성했습니다.
// 2: Rc 타입의 인스턴트 메서드인 clone으로 rc의 클론을 생성하면서 Vec에 저장했습니다.
클론을 생성할 때마다 소유권을 공유하는 숫자가 늘어납니다.
// 3: while 문에서 Vec 타입의 인스턴트 메소드인 pop으로 하나씩 꺼내면서 삭제해 나갑니다.
그 과정에서 Rc 타입의 메소드인 strong_count를 통해 rc의 카운팅을 출력합니다.
// 4: vec의 모든 원소가 pop으로 제거되고 원본 rc만 남은 상태에서는 카운팅이 1인 것을 확인할 수 있습니다.
주의할 점이 있습니다. Rc는 불변입니다.
즉 여러 클론 중 어느 한 클론에서 데이터의 변경을 시도할 수 없습니다.
2) Weak
Rc 타입의 변수에는 한 가지 약점이 있습니다.
클론을 생성하면서 서로가 서로의 클론이 되는 상황이 발생할 수 있습니다.
이런 상황을 cycle이라고 합니다. 즉 a 변수가 b를 클론하고, b 변수가 c를 클론 하고, c 변수가 a를 클론하는 것과 같은 상황입니다.
이런 상황을 방지하기 위해 Weak 타입이 고안되었습니다.
Weak 타입은 Rc 타입을 downgrade 시켜서 만듭니다.
let rc = Rc::new(10);
println!("strong count: {}", Rc::strong_count(&rc));
let rc1 = Rc::downgrade(&rc); // 1
let rc2 = Rc::downgrade(&rc);
// 2
println!("weak count: {}", Rc::weak_count(&rc));
println!("weak: {:?}", rc1); // 3
let rc3 = rc1.upgrade(); // 4
println!("{:#?}", rc3); // 5
println!("strong count: {}", Rc::strong_count(&rc)); // 6
// 출력
strong count: 1
weak count: 2
weak: (Weak)
Some(
10,
)
strong count: 2
// 1: Rc::downgrade() 메서드를 통해서 rc의 Weak 타입을 만들었습니다.
// 2: Rc::weak_count로 Weak 타입의 카운팅을 할 수 있습니다.
// 3: Weak 타입은 어떤 값을 가지고 있는지 보기 위해 출력을 해봤더니
weak: (Weak)
이 출력되었습니다. Weak은 그 자체로 아무런 역할을 하지 못 합니다.
// 4: 대신 다시 upgrade 하면 원래의 값을 사용할 수 있습니다.
// 5: Weak 타입에서 다시 upgrade될 때는 Result 타입으로 돌려줍니다. 그래서 위 코드에서도 upgrade된 값을 출력하면
Some(
10,
)
형식으로 출력됩니다.
// 6: 이때 strong_count로 확인해 보면 카운트가 늘어나 있습니다.