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