🔑

소유권과 빌림 — Rust가 GC 없이 메모리를 관리하는 방법

컴파일 타임에 메모리 안전성을 보장하는 Rust의 핵심 규칙

C/C++는 프로그래머가 메모리를 직접 관리한다. 실수하면 use-after-free, double free, 댕글링 포인터 — 보안 취약점의 70%가 여기서 나온다(Microsoft 통계). Java/Go/Python은 가비지 컬렉터(GC)로 해결하지만, GC 일시 정지와 메모리 오버헤드가 따라온다.

Rust는 제3의 방법을 택했다. 컴파일러가 소유권 규칙을 검사해서 메모리 버그를 코드 작성 시점에 잡는다. 런타임 비용이 0이다.

세 가지 규칙

1. 값에는 소유자가 하나만 존재한다. let s2 = s1;을 쓰면 s1의 소유권이 s2로 이동(move)한다. 이후 s1을 쓰면 컴파일 에러.

2. 빌림(borrow)은 두 종류. &x는 불변 참조(여러 개 동시 가능), &mut x는 가변 참조(하나만). 둘을 동시에 쓸 수 없다.

3. 참조는 소유자보다 오래 살 수 없다. 이게 라이프타임 규칙이고, 댕글링 포인터를 원천 차단한다.

처음 쓰면 뭐가 힘든가

컴파일러가 에러를 내뿜는 빈도에 멘탈이 흔들린다. 특히 구조체에 참조를 넣으려 할 때 라이프타임 명시 요구가 처음엔 벽처럼 느껴진다. 근데 이건 다른 언어에서 "런타임에 터지던 버그"를 컴파일 타임으로 끌어온 것이다. 에러 위치가 바뀐 거지 버그 수가 늘어난 게 아니다.

핵심 포인트

1

모든 값에는 소유자(owner)가 정확히 하나 존재한다

2

소유자가 스코프를 벗어나면 값이 자동으로 drop된다 (RAII)

3

불변 참조(&T)는 여러 개 동시 가능, 가변 참조(&mut T)는 하나만

4

참조가 유효한 동안 소유자는 값을 이동하거나 가변 빌림할 수 없다

5

컴파일러(borrow checker)가 이 규칙을 모두 정적으로 검증한다

장점

  • 메모리 버그가 컴파일 타임에 잡힌다 — 런타임 크래시 대신 컴파일 에러
  • GC 없음 — 런타임 오버헤드 제로, 예측 가능한 성능
  • data race 방지 — 컴파일러가 동시 가변 접근을 차단

단점

  • 학습 곡선이 가파르다 — borrow checker와의 싸움이 초반 허들
  • 자기 참조 구조체(self-referential struct)가 기본적으로 불가능
  • 프로토타이핑 속도가 GC 언어 대비 느리다

사용 사례

시스템 프로그래밍 — OS 커널, 드라이버에서 메모리 안전성 보장 웹 서버 — GC 일시 정지 없는 예측 가능한 응답 시간 임베디드 — 힙 할당 없이 스택만으로 동작하는 코드 작성