원문 캡처 · cirw.in
원문 캡처 · cirw.in

Rust의 가장 좋은 기능은 하나의 가변 참조만 허용하는 점이다.

Rust는 이를 대여 검사기(borrow checker)로 구현한다. 각 변수는 소유된 값이거나 공유(불변) 참조이거나 가변 대여 상태다.

Rust의 참조가 흥미로운 점은 두 가지 역할을 한다는 것이다. 값을 변경할 권한이 있는지 추적하고, 값을 통째로 전달하는 대신 참조로 전달하게 한다.

가상의 더 단순한 언어에서는 참조로 전달할지 여부를 컴파일러가 결정할 수 있다. 대부분의 경우 최신 ARM 프로세서에서 24바이트처럼 일정 크기보다 큰 구조체는 참조로, 더 작은 값은 값으로 전달하면 된다.

자바스크립트 같은 언어도 비슷한 방식을 쓴다. ‘원시’ 타입은 항상 값으로 전달하고 ‘객체’ 타입은 항상 참조로 전달한다. 하지만 이런 언어에서는 객체 참조를 가진 쪽이 객체를 변경할 수 있다. 하나의 가변 소유권을 강제하지 않는다.

스위프트는 다른 길을 택한다. 가변 참조 대신 inout 매개변수를 쓸 수 있다. 개념적으로는 값 전체를 함수에 전달한 다음, 호출이 끝날 때 수정됐을 수도 있는 값 전체를 돌려받는 방식이다.

이 접근법의 장점은 겉보기에는 가변 참조가 필요 없어 보인다는 점이다. 이 의미를 호출을 문법 변환(desugaring)하는 방식으로 표현할 수 있다. fn a(inout b)는 fn a(b) -> b로 다시 쓸 수 있고, 모든 호출도 같은 방식으로 바꿀 수 있다.

Rust 방식은 스위프트 방식보다 다른 구조체를 참조하는 필드를 가진 구조체를 만들기 쉽다는 장점도 있다. 이런 구조는 특히 반복자(Iterator)를 구현할 때 쓰이지만, 그 밖에는 비교적 드물다.

이 문제도 같은 방식으로 풀 수 있다고 본다. struct Foo { bar: inout Bar }를 생각해볼 수 있다. bar 필드는 Bar 값의 소유권을 임시로 넘겨받지만, 끝날 때 값을 돌려줘야 한다는 뜻이다. 다만 이 경우 메모리 배치 문제를 감수할 수 있을지는 분명하지 않다. 구조체와 배열은 원소 수가 훨씬 많을 수 있어 호출 규약보다 메모리 배치가 더 중요해지는 경향이 있다.

그래도 일단 지켜보는 편이 나을 수 있다. Rust에서도 참조 배열은 비교적 드물다. 따라서 이런 처리를 언어가 대신 맡는 것이 전반적으로 더 단순한 방식일 수 있다.