본문 바로가기

Rust

[Rust] 스마트 포인터 (2) - RefCell<T>, Arc<T>, Mutex<T>

지난 포스터에 이어서 이번엔

RefCell<T>, Arc<T>, Mutex<T>에 대해 알아보겠습니다.


📌 Rc<T>의 한계

Rc<T>는 아래 2가지 한계가 있습니다.

  1. 가변성
  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
}