스마트 포인터

_포인터_는 메모리 주소를 담고 있는 변수를 통칭하는 개념이다. 이 주소는 특정 데이터를 가리키거나 참조한다. Rust에서 가장 흔히 사용하는 포인터는 4장에서 배운 참조(reference)다. 참조는 & 기호로 표시되며, 가리키는 값을 빌려온다. 참조는 데이터를 참조하는 기능 외에는 특별한 능력이 없고, 오버헤드도 없다.

반면 _스마트 포인터_는 포인터처럼 동작하면서 추가 메타데이터와 기능을 제공하는 데이터 구조다. 스마트 포인터는 Rust에만 있는 개념이 아니다. C++에서 처음 등장했으며 다른 언어에서도 존재한다. Rust의 표준 라이브러리에는 참조보다 더 다양한 기능을 제공하는 여러 종류의 스마트 포인터가 정의되어 있다. 이번 장에서는 스마트 포인터의 일반적인 개념을 이해하기 위해 몇 가지 예를 살펴볼 것이다. 그 중 하나는 참조 카운팅 스마트 포인터 타입이다. 이 포인터는 데이터의 소유자 수를 추적하고, 소유자가 없어지면 데이터를 정리함으로써 데이터가 여러 소유자를 가질 수 있게 한다.

Rust의 소유권과 빌림 개념은 참조와 스마트 포인터 사이에 또 다른 차이를 만든다. 참조는 데이터를 빌려오지만, 스마트 포인터는 대부분의 경우 가리키는 데이터를 _소유_한다.

이 책에서 이미 몇 가지 스마트 포인터를 접했지만, 당시에는 그렇게 부르지 않았다. 8장에서 다룬 StringVec<T>가 그 예시다. 이 두 타입은 메모리를 소유하고 이를 조작할 수 있기 때문에 스마트 포인터로 분류된다. 또한 메타데이터와 추가 기능 또는 보장을 제공한다. 예를 들어, String은 용량을 메타데이터로 저장하며, 데이터가 항상 유효한 UTF-8임을 보장하는 추가 기능을 갖는다.

스마트 포인터는 보통 구조체(struct)로 구현된다. 일반 구조체와 달리, 스마트 포인터는 DerefDrop 트레이트를 구현한다. Deref 트레이트는 스마트 포인터 구조체의 인스턴스가 참조처럼 동작하게 해, 코드가 참조나 스마트 포인터 둘 다와 호환되도록 한다. Drop 트레이트는 스마트 포인터 인스턴스가 스코프를 벗어날 때 실행될 코드를 커스텀할 수 있게 한다. 이번 장에서는 이 두 트레이트를 자세히 살펴보고, 스마트 포인터에서 왜 중요한지 설명할 것이다.

스마트 포인터 패턴은 Rust에서 자주 사용되는 일반적인 디자인 패턴이므로, 이번 장에서는 모든 스마트 포인터를 다루지 않는다. 많은 라이브러리가 자체 스마트 포인터를 제공하며, 직접 스마트 포인터를 작성할 수도 있다. 이번 장에서는 표준 라이브러리의 가장 흔히 사용되는 스마트 포인터를 다룰 것이다:

  • Box<T>: 힙에 값을 할당하기 위한 포인터
  • Rc<T>: 다중 소유권을 가능하게 하는 참조 카운팅 타입
  • Ref<T>RefMut<T>: RefCell<T>를 통해 접근하며, 컴파일 타임이 아닌 런타임에 빌림 규칙을 강제하는 타입

또한, 불변 타입이 내부 값을 변경할 수 있는 API를 제공하는 내부 가변성 패턴도 살펴볼 것이다. 그리고 참조 순환 문제가 어떻게 메모리 누수를 일으키는지, 그리고 이를 방지하는 방법에 대해서도 논의할 것이다.

이제 시작해 보자!