c/c++
에서는 메모리를 할당 후 해제를 직접 해줘야 한다.
스마트 포인터를 사용하면 수동으로 메모리를 해제해 줄 필요가 없다.
즉, 안전하게 메모리를 사용할 수 있도록 도와준다.
스마트 포인터에 대해 쉽게 이해할 수 있도록 식당을 예로 들어보겠습니다.
우리가 식당에서 음식을 먹는다고 상상해 봅시다.
음식
은 메모리
라고 생각할 수 있습니다.
1. 셀프 매장에서는 음식을 먹은 후 사용한 식기를 우리가 직접 반납해야 합니다.
즉, 이용자가 직접 메모리를 해제하는 것과 같습니다.
2. 반면에 일반 식당에서는 음식을 먹은 후 그냥 자리를 떠나도 됩니다.
식당 직원들이 알아서 사용한 식기를 치워줍니다.
이는 스마트 포인터가 메모리를 자동으로 해제 해주는 방식과 비슷합니다.
스마트 포인터를 사용하면 프로그래머는 메로리를 사용하는 데 집중할 수 있고, 메모리를 해제하는 번거로운 작업은 스마트 포인터가 대신 처리해줍니다.
이제 Rust에서 스마트 포인터가 어떻게 동작하는지 알아보겠습니다.
가장 단순한 스마트 포인터이다. 데이터를 힙(Heap)
에 저장하고 소유권을 관리합니다.
단일 소유권을 가집니다.
let a : Box<i32> = Box::new(10);
assert_eq!(*a, 10);
*
를 사용하여 역참조한다.
Rc는 여러 소유자를 가능하게 하는 스마트 포인터입니다. 데이터를 힙에 저장하며, 참조 카운터를 통해 소유권을 관리합니다.
불변 참조만 가능합니다.
Box
와 Rc
의 메모리 주소로 차이에 대해서 알아보겠습니다.
let a : Box<i32> = Box::new(30);
let b = Box::clone(&a);
let c = Box::clone(&a);
println!("a 주소 : {:p}", a);
println!("b 주소 : {:p}", b);
println!("c 주소 : {:p}", c);
주소값이 전부 다른걸 볼 수 있습니다.
그렇다면 Rc
의 경우를 한번 보겠습니다.
let a : Rc<i32> = Rc::new(30);
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("a 주소 : {:p}", a);
println!("b 주소 : {:p}", b);
println!("c 주소 : {:p}", c);
println!("참조 count : {}", Rc::strong_count(&a));
주소값이 동일하게 나온 걸 볼 수 있습니다.
a
의 값을 b
와 c
가 참조한다고 볼 수 있습니다. 또한 참조 카운터가 3
인 걸 볼 수 있습니다.
결론적으로 Box
의 경우는 단일 소유권을 가지고 있고, Rc
다중 소유권을 가질 수 있습니다. Rc
의 경우 DerefMut
를 구현하지 않아 가변 역참조가 불가능하여 값을 수정할 수 없습니다.
Arc : Atomic Reference Counted의 약자로, Rust의 스마트 포인터 중 하나이다.
Rc와 비슷하지만, 스레드 간 공유를 지원하도록 설계하였다.
Arc는 멀티 스레드에 대해서 다룰 때 자세히 살펴볼 예정입니다.