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);
}