Rust 입문

[Rust] Rc와 Weak

바로크냥 2022. 10. 21. 06:30

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로 확인해 보면 카운트가 늘어나 있습니다.

 

 
반응형