RefCell<T>

Rc<T>의 한계

Rc를 사용하면 프로그램의 여러 부분에서 읽기 전용으로 데이터를 공유할 수 있습니다. 하지만 Rc가 불변 레퍼런스를 통해 값을 공유하기 때문에, 공유받은 값을 변경하는 것은 불가능합니다. 아래 예시를 살펴봅시다.

use std::rc::Rc;

struct Owner {
    name: String,
    tools: Rc<Vec<Rc<Tool>>>,
}

struct Tool {
    owner: Rc<Owner>,
}

pub fn main() {
    let indo = Rc::new(Owner {
        name: "indo".to_string(),
        tools: Rc::new(vec![]),
    });
    let pliers = Rc::new(Tool {
        owner: Rc::clone(&indo),
    });
    let wrench = Rc::new(Tool {
        owner: indo.clone(),
    });

    indo.tools.push(Rc::clone(&pliers)); // 🤯
    indo.tools.push(Rc::clone(&wrench));

    println!("Pliers owner: {}", pliers.owner.name);

    for tool in indo.tools.iter() {
        println!("Tool's owner: {:?}", tool.owner.name);
    }
}

실행 결과

error[E0596]: cannot borrow data in an `Rc` as mutable
  --> src/main.rs:24:5
   |
24 |     brad.tools.push(Rc::clone(&pliers)); // 🤯
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   |

...
use std::{cell::RefCell, rc::Rc};

struct Owner {
    name: String,
    tools: RefCell<Vec<Rc<Tool>>>,
}

struct Tool {
    owner: Rc<Owner>,
}

pub fn main() {
    let indo = Rc::from(Owner {
        name: "indo".to_string(),
        tools: RefCell::new(vec![]),
    });
    let pliers = Rc::from(Tool {
        owner: Rc::clone(&indo),
    });
    let wrench = Rc::from(Tool {
        owner: indo.clone(),
    });

    indo.tools.borrow_mut().push(Rc::clone(&pliers));
    indo.tools.borrow_mut().push(Rc::clone(&wrench));

    println!("Pliers owner: {}", pliers.owner.name);

    for tool in indo.tools.borrow().iter() {
        println!("Tool's owner: {:?}", tool.owner.name);
    }
}

내부 가변성(Interiror mutability)

RefCell<T>가 불변이어도 내부의 값은 가변으로 사용 가능

#![allow(unused)]
fn main() {
indo.tools.borrow_mut().push(Rc::clone(&pliers));
}

불변 소유권 대여도 가능

#![allow(unused)]
fn main() {
indo.tools.borrow().iter()
}

소유권 규칙

  • 여러 번 빌려도 괜찮습니다
  • 한 번 빌리는 것도 괜찮습니다
  • 하지만 가변과 불변이 대여는 불가능합니다

런타임 시간에 소유권이 확인되기 때문에 컴파일이 되지만 런타임 에러 발생

use std::{cell::RefCell, rc::Rc};

struct Owner {
    name: String,
    tools: RefCell<Vec<Rc<Tool>>>,
}

struct Tool {
    owner: Rc<Owner>,
}

pub fn main() {
    let indo = Rc::from(Owner {
        name: "indo".to_string(),
        tools: RefCell::new(vec![]),
    });
    let pliers = Rc::from(Tool {
        owner: Rc::clone(&indo),
    });
    let wrench = Rc::from(Tool {
        owner: indo.clone(),
    });

    let mut borrow_mut_tools1 = indo.tools.borrow_mut();
    let mut borrow_mut_tools2 = indo.tools.borrow_mut(); // 🤯
    borrow_mut_tools1.push(Rc::clone(&pliers));
    borrow_mut_tools2.push(Rc::clone(&wrench));

    println!("Pliers owner: {}", pliers.owner.name);

    for tool in indo.tools.borrow().iter() {
        println!("Tool's owner: {:?}", tool.owner.name);
    }
}

실행 결과

thread 'main' panicked at 'already borrowed: BorrowMutError', src/main.rs:25:44

Rc<RefCell<T>>

RefCell<T>를 사용하는 일반적인 방법은 Rc<T>와 함께 사용하는 것입니다. Rc<T>를 사용하면 일부 데이터의 소유자를 여러 명 가질 수 있지만, 해당 데이터에 대한 불변 액세스 권한만 부여한다는 점을 기억하세요. RefCell<T>를 보유한 Rc<T>가 있다면, 여러 소유자를 가질 수 있고 변경할 수 있는 값을 얻을 수 있습니다!

요약하자면, Rc는 공유 소유권을 제공합니다. 내부 값에는 여러 소유자가 있으며, 참조 카운팅은 적어도 한 명의 소유자가 데이터를 계속 보유하고 있는 한 데이터가 계속 유지되도록 합니다. 이는 데이터 소유자가 명확하지 않은 경우에 유용합니다. RefCell은 내부 가변성을 제공합니다. 즉, 런타임에 내부 값을 동적으로 빌릴 수 있고 공유 참조를 통해서도 수정할 수 있습니다. Rc<RefCell<...>> 조합은 소유자가 여러 명인 값을 소유자 중 한 명이 가변적으로 빌릴 수 있는 두 가지의 조합을 제공합니다.

언제 무엇을

Box<T>Rc<T>RefCell<T>
소유권한 개한 개를 공유한 개
소유권 확인 시점불변/가변 소유권을 컴파일 타임에 확인불변 소유권을 컴파일 타임에 확인불변/가변 소유권을 런타임에 확인
특징스코프를 벗어나면 레퍼런스도 모두 삭제레퍼런스가 존재한다면 스코프를 벗어나도 값이 유지됨RefCell<T>가 불변이어도 내부의 값은 가변으로 사용 가능

RefCell는 멀티스레드 코드에서는 작동하지 않는다는 점에 유의하세요! Mutex는 스레드에 안전한 RefCell<T의 버전이며, Mutex<T에 대해서는 나중에 설명하겠습니다.

Quiz

use std::fmt::Display;
use std::vec::Vec;

#[derive(Debug)]
struct Node<T> {
    data: T,
    children: Vec<Node<T>>,
}

impl<T: Display> Node<T> {
    fn new(data: T) -> Node<T> {
        Node {
            data,
            children: Vec::new(),
        }
    }

    fn depth_first(&self) {
        println!("{}", self.data);
        for child in self.children.iter() {
            child.depth_first();
        }
    }
}

fn main() {
    let mut a = Node::new('A');
    let mut b = Node::new('B');
    let c = Node::new('C');
    let d = Node::new('D');

    b.children.push(d);
    a.children.push(b);
    a.children.push(c);

    a.depth_first();
}
#![allow(unused)]
fn main() {
  fn add_child(&mut self, child: Wrapper<Node<T>>) {
      self.children.push(child);
  }
}
fn main() {
    let a = wrap(Node::new('A'));
    let b = wrap(Node::new('B'));
    let c = wrap(Node::new('C'));
    let d = wrap(Node::new('D'));

    a.borrow_mut().add_child(Rc::clone(&b));
    a.borrow_mut().add_child(Rc::clone(&c));
    b.borrow_mut().add_child(Rc::clone(&d));
    a.borrow_mut().depth_first();
}

정답

use std::cell::RefCell;
use std::fmt::Display;
use std::rc::Rc;
use std::vec::Vec;

type Wrapper<T> = Rc<RefCell<T>>;

fn wrap<T>(data: T) -> Wrapper<T> {
    Rc::new(RefCell::new(data))
}

#[derive(Debug)]
struct Node<T> {
    data: T,
    children: Vec<Wrapper<Node<T>>>,
}

impl<T: Display> Node<T> {
    fn add_child(&mut self, child: Wrapper<Node<T>>) {
        self.children.push(child);
    }

    fn new(data: T) -> Node<T> {
        Node {
            data,
            children: Vec::new(),
        }
    }

    fn depth_first(&self) {
        println!("node {}", self.data);
        for child in self.children.iter() {
            child.borrow().depth_first();
        }
    }
}

fn main() {
    let a = wrap(Node::new('A'));
    let b = wrap(Node::new('B'));
    let c = wrap(Node::new('C'));
    let d = wrap(Node::new('D'));

    a.borrow_mut().add_child(Rc::clone(&b));
    a.borrow_mut().add_child(Rc::clone(&c));
    b.borrow_mut().add_child(Rc::clone(&d));
    a.borrow_mut().depth_first();
}