목차
- Cell 타입과 RefCell 타입
- Cell 타입 사용하기
- RefCell 타입 사용하기
Cell 타입과 RefCell 타입
이전에 작성했던 Rc 타입과 Weak 타입 - Rust 프로그래밍에서는 어떤 값에 대해 여러 소유자를 가질 수 있게 해주는 스마트 포인터 Rc에 대해 살펴봤습니다. 이번 글에서는 불변 변수를 가변 변수처럼 사용할 수 있게 해주는 Cell과 RefCell 타입에 대해 살펴보겠습니다.
Rust의 참조 규칙에 따르면, 어떤 객체에 대한 불변 참조자는 여러 개 가질 수 있고, 가변 참조자는 하나만 가질 수 있습니다. 또한, 이 두 가지 규칙은 동시에 적용할 수 없습니다. 이런 규칙을 깰 필요가 있는 상황에서 Cell과 RefCell 스마트 포인터 타입을 사용할 수 있습니다. Cell과 RefCell 타입은 모두 단일 스레드 환경에서만 가변성을 보장할 수 있으며, 다중 스레드 환경을 위한 가변성은 Mutex, RwLock, atomic과 같은 타입을 참고해보세요.
대부분의 Rust 타입들은 가변 참조자(&mut T)를 통해서만 값을 변경할 수 있습니다. Cell와 RefCell를 이용하면 불변 참조자(&T)를 통해서 값을 변경할 수 있게 됩니다. Rust에서는 이를 내부 가변성을 제공한다고 이야기합니다.
Cell 타입은 값의 이동에 따른 내부 가변성을 구현합니다. 따라서, 값이 아닌 참조를 사용하려면 RefCell 타입을 사용해야 합니다. RefCell 타입은 dynamic borrowing
을 구현하기 위해 lifetime을 사용합니다. dynamic borrowing
은 내부의 값에 일시적인 가변 접근을 가능하게 합니다. Rust의 네이티브 참조 타입들은 컴파일 타임에 정적으로 추적되지만, RefCell 타입은 런타임에 동적으로 추적됩니다. 이처럼 RefCell의 borrowing은 동적이기 때문에, 가변 참조자가 이미 존재하더라도 런타임에 값을 또 빌리는 시도를 할 수 있지만 패닉을 발생시킬 것입니다.
Cell 타입 사용하기
fn main() {
let cell = Cell::new(1);
println!("before : {}", cell.get());
cell.set(2);
println!("after : {}", cell.get());
}
위 코드는 Cell 타입의 기본적인 사용법입니다. Cell 타입은 get()
메서드를 통해 값을 가져올 수 있고, set()
메서드를 통해 값을 변경할 수 있습니다. 앞서 설명했지만, Cell 타입은 값의 이동에 따른 구현입니다. 즉, get() 메서드를 호출하면 값이 리턴됩니다. 만약, 참조자가 필요하면 RefCell 타입을 이용해야 합니다.
아래에서 좀 더 자세히 설명하겠습니다.
RefCell 타입 사용하기
fn main() {
let ref_cell = RefCell::new(1);
let borrow_one = ref_cell.borrow();
let borrow_two = ref_cell.borrow();
println!("{}", borrow_one);
println!("{}", borrow_two);
}
위 코드는 RefCell 타입의 기본적인 사용법입니다. 불변 참조자를 얻기 위해 borrow()
메서드를 사용하는 것을 볼 수 있습니다.
아래 코드에서는 가변 참조자를 얻기 위해 borrow_mut()
메서드를 사용합니다.
fn main() {
let ref_cell = RefCell::new(1);
*ref_cell.borrow_mut() += 1;
println!("{}", ref_cell.borrow());
}
우리는 불변 변수 ref_cell
을 가변 변수처럼 사용하기 위해 RefCell을 사용하지만, RefCell의 내부에서는 Rust의 참조 규칙이 동일하게 적용됩니다. 즉, borrow() 메서드를 통해 불변 참조자가 존재할 때, borrow_mut()
를 통해 가변 참조자를 얻으려고 하면 프로그램은 패닉을 일으킵니다.
따라서, 아래의 코드는 실행 도중 패닉이 발생합니다.
fn main() {
let ref_cell = RefCell::new(1);
let borrow_one = ref_cell.borrow();
let borrow_two = ref_cell.borrow();
*ref_cell.borrow_mut() += 1;
println!("{}", ref_cell.borrow());
}
References
'Dev > Language' 카테고리의 다른 글
인자로 &String보다 &str을 사용하라 - Rust 프로그래밍 (0) | 2023.03.05 |
---|---|
impl Trait과 Box<dyn Trait> - Rust 프로그래밍 (1) | 2022.12.19 |
Rc 타입과 Weak 타입 - Rust 프로그래밍 (4) | 2022.10.29 |
소유권(Ownership)과 원시 타입 - Rust 프로그래밍 (0) | 2022.08.29 |
트레잇(Trait)과 트레잇 바운드(Trait Bound) - Rust 프로그래밍 (1) | 2022.08.25 |