지난 포스터에 이어서 이번엔
RefCell<T>, Arc<T>, Mutex<T>에 대해 알아보겠습니다.
📌 Rc<T>의 한계
Rc<T>는 아래 2가지 한계가 있습니다.
- 가변성
- 멀티스레드
이를 해결해주는 스마트 포인터로 RefCell<T>, Arc<T>가 있습니다.
📌 RefCell<T>
✅ 개념
런타임에 가변성을 허용하는 스마트 포인터
- RefCell<T>를 사용해서 가변성을 사용할 수 있습니다
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
*data.borrow_mut() += 10; // 런타임에서 가변 빌림
println!("data = {}", data.borrow()); // data = 15
}
borrow_mut()을 통해 불변 변수를 가변으로 변경 가능합니다
✅ Rc<T> + RefCell<T> 조합
Rc<T>는 불변성만 허용하는 스마트 포인터 였지만,
여러 소유자가 동일하게 사용하면서 가변성을 제공하고 싶을 때 이렇게 사용할 수 있습니다.
use std::rc::Rc;
use std::cell::RefCell;
struct Node {
value: RefCell<i32>,
next: Option<Rc<Node>>,
}
fn main() {
let node1 = Rc::new(Node {
value: RefCell::new(10),
next: None,
});
let node2 = Node {
value: RefCell::new(20),
next: Some(Rc::clone(&node1)),
};
*node1.value.borrow_mut() += 10;
*node2.value.borrow_mut() += 10;
println!("node1의 값: {}", node1.value.borrow()); // 출력: 20
println!("node2의 값: {}", node2.value.borrow()); // 출력: 30
}
📌 Arc<T>
✅ 개념
멀티스레드 환경에서 안전한 참조 카운팅
- 멀티스레드 환경에서 여러 개의 소유자가 하나의 데이터를 공유할 수 있도록 해줍니다.
- 내부적으로 원자적 연산 (atomic operations)을 사용해서 스레드 안전성을 보장합니다.
use std::sync::Arc;
use std::thread;
fn main() {
let a = Arc::new(String::from("Hello, Arc!")); // Arc 스마트 포인터 생성
let a1 = Arc::clone(&a);
let a2 = Arc::clone(&a);
let handle1 = thread::spawn(move || {
println!("Thread 1: {}", a1); // Thread 1: Hello, Arc!
});
let handle2 = thread::spawn(move || {
println!("Thread 2: {}", a2); // Thread 2: Hello, Arc!
});
handle1.join().unwrap();
handle2.join().unwrap();
}
✅ 가변성이 필요할 땐?
Arc<T>는 불변성만 제공하는 스마트 포인터 입니다.
그래서 다른 스마트 포인터를 사용해야 합니다.
Mutex<T> 사용
- 멀티 스레드 환경에서 동시 접근을 막고 안전하게 데이터를 변경할 수 있도록 보장합니다.
use std::sync::Mutex;
fn main() {
let data = Mutex::new(5);
{
let mut num = data.lock().unwrap(); // 락을 얻음
*num += 1;
} // 락이 자동으로 해제됨
println!("Result: {}", data.lock().unwrap()); // 6
}
Arc<T> + Mutex<T> 조합
- 멀티 스레드 환경에서 공유 데이터를 가변적으로 사용하고 싶을 때 사용합니다.
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0)); // Arc<Mutex<T>>로 공유
let handles: Vec<_> = (0..3)
.map(|_| {
let data = Arc::clone(&data);
thread::spawn(move || {
let mut num = data.lock().unwrap(); // 락 획득
*num += 1; // 데이터 증가
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
println!("Final Result: {}", *data.lock().unwrap()); // 3
}
'Rust' 카테고리의 다른 글
[Rust] 에러 처리 (0) | 2025.03.30 |
---|---|
[Rust] 스마트 포인터 (1) - Box<T>, Rc<T> (2) | 2025.03.24 |
[Rust] 제네릭(Generic), 트레잇(Trait), 동적 디스패처(dynamic dispatch) (2) | 2025.03.11 |
[Rust] 소유권, 참조와 빌림, 슬라이스 (0) | 2025.03.03 |
[Rust] 기초 문법 (2) | 2025.02.28 |