요약
Rust 언어팀이 직접 만든 스마트 포인터도 내장 참조 &처럼 다루게 하는 설계를 시험하고 있습니다.
LWN이 RustConf 2026에서 Rust 언어팀 리드 타일러 맨드리가 한 발표를 정리했습니다. 발표는 컴파일러가 내장 참조에만 주는 편의를 라이브러리의 스마트 포인터(포인터처럼 쓰이면서 기능을 더한 타입)에도 열어 주자는 프로젝트 목표 "Beyond the &"를 다뤘습니다.
왜 중요한가
- 포인터 종류를 바꿨다는 이유만으로 멀쩡한 코드가 컴파일되지 않는 불일치를 없애려는 시도입니다.
- 맨드리는 이런 불일치가 Rust를 배우기 어렵게 만드는 원인 가운데 하나라고 봤습니다.
- 라이브러리 작성자가 자기 포인터의 의미를 컴파일러에 직접 알릴 수 있게 됩니다.
핵심 내용
&mut RenderState로는 되는 캐시 코드가Mutex로 감싸면 두 필드가 겹치지 않음을 증명하지 못해 거부됩니다.- 해법은 컴파일러 내부의 플레이스(값을 읽고 쓰는 위치, C의 lvalue에 해당) 개념을 사용자 코드에 드러내는 것입니다.
- 포인터마다 핸들 타입을 두고 ReadPlace, WritePlace, ProjectPlace, BorrowPlace 같은 트레이트(타입이 구현하는 인터페이스)로 동작을 정의합니다.
MutexGuard에 핸들을 구현하면 예제가 재빌림(&mut *로 다시 빌리기) 없이 컴파일되고, 포인터 너머의 패턴 매칭도 가능해집니다.- 아직 시제품 단계이며 빌림 문법(
&재사용이나@), 오류 메시지, 열거형 필드 접근 같은 과제가 남아 있습니다.
HN 반응
- 재빌림 한 줄이면 되는데 암묵적 동작을 늘릴 필요가 있느냐는 반론과, 내장 포인터만 특별 대우하는 지금이 더 헷갈린다는 반박이 맞섰습니다.
- 잠금을 뜻하는
MutexGuard를 필드마다 만들면 안 된다는 비판에, 잠금을 유지한 채 필드만 넘길 수 있어 쓸모 있다는 의견이 나왔습니다.
"place는 C의 좌변값(lvalue)에 해당하는 Rust의 개념으로, 값을 읽거나 쓸 수 있는 위치를 뜻합니다. place와 포인터의 차이는, place는
state.cache처럼 컴파일 시점에 존재하는 추상적인 표현식이고, 포인터는 place를 런타임에 나타내는 한 가지 방법이라는 점입니다."도움이 된 대목이었습니다.
멋진 시도이긴 한데, MutexGuard 예시는 솔직히 설득력이 없다고 봐요. 기사에는 이렇게 나와 있죠.
제발 그러지 맙시다. 기사 뒷부분의 예시에서는 Option<&A>를 역참조하고 프로젝션해서 A 안에 있는 무언가에 대한 옵셔널 참조를 얻을 수도 있겠죠. 그건 괜찮아요. Option은 논리적으로 그냥 값이고, 참조가 있거나 없거나 둘 중 하나니까요.
하지만 MutexGuard는 단순한 값이 아니잖아요. 특정 뮤텍스를 잡고 있는 락을 나타내는 표현입니다. 그리고 drop하면 부수 효과가 생기고요! MutexGuard<RenderState>가 있다는 건 Mutex<RenderState>가 있다는 뜻이고, 가드가 drop되면 그 뮤텍스가 풀립니다. RenderState의 필드는 자기만의 뮤텍스가 있지 않은 이상 가드를 가지면 안 돼요!
제가 보기에 뮤텍스 가드의 필드 프로젝션에서 정말 필요한 건 다음 둘 중 하나예요.
좋네요! 그럼 문법을 개선하면 됩니다. 아니면 이런 방법도 있어요.
놓치신 부분이 있는 것 같습니다. MutexGuard는 소유권을 가진 값입니다. 뮤텍스 가드 프로젝션의 이점이 바로 그것입니다. 프로젝션 결과가 소유 타입이라서 락을 계속 잠근 상태로 유지해 줍니다. 필드 프로젝션이 참조를 반환하게 하면 쓸모가 없습니다. 그건 이미 지금도 잘 되기 때문입니다. 바로 deref죠. 하지만 소유권을 가진 참조가 필요할 때는 그 방법이 통하지 않습니다. Arc 같은 다른 모든 것의 필드 프로젝션도 마찬가지입니다.
저는 동의하지 않습니다. 원래의 뮤텍스는 가드를 통해 그 필드에 접근하게 해 주는 것이고, (접근 목적에 한해서) 가드와 필드를 계속 들고 있으면서 그 주변의 제약만 걷어내는 것은 문제될 게 없습니다. 뮤텍스 가드가 자기 뮤텍스와 정적으로 동일성 관계를 맺고 있는 것도 아니고요.
음. 기사의 예시에는 .cache와 .template에 대한 참조가 서로 겹치는 상황이 나옵니다. 소유권을 가진 가드 프로젝션이 거기서 어떻게 말이 되는지 저는 잘 모르겠습니다.
RenderState 전체에 대한 MutexGuard를 하나 들고 있다가 그중 일부인 MutexGuard<PartOfTheState>를 소유권째 다른 곳에 넘기는 건 어렴풋이 상상이 됩니다. 그래도 같은 뮤텍스를 잡고 있는 다른 가드와 공존할 수 있어서는 안 될 것 같습니다.
그리고 가드를 쪼갠다는 건 애초에 어떻게 동작합니까? 뮤텍스의 각 부분에 참조 카운트를 두는 겁니까? 뮤텍스가 그 기능을 쓰겠다고 직접 선택해야 합니까? 뮤텍스 가드를 쪼개는 게 실제로 쓸모 있는 예시가 정말 있기는 합니까!
발표 영상입니다: https://www.youtube.com/watch?v=bXs56902ElY
제가 보기엔 멀쩡한데요? 코드가 실패하는 이유는 MutexGuard를 두 번 역참조하기 때문이고, 그러니 한 번만 역참조하면 되는 거잖아요. 이게 "Rust의 가파른 학습 곡선"에 크게 기여한다는 건 상상하기 어렵네요.
오히려 모든
.마다 암묵적으로 일어나는 일을 더 늘리면 언어를 배우기가 더 어려워질 것 같아요.이런 기능들로 정말 이득을 보는 예시가 있나요?
문제는 내장 포인터(참조와 box)가 이미 컴파일러에서 특별 취급되어 알아서 척척 동작한다는 점입니다. 그래서 그런 내장 타입과 서드파티 포인터, 심지어 컴파일러가 특별 취급하지 않는 표준 라이브러리 포인터 사이를 오갈 때 혼란이 생깁니다.
그러니 이건 "모든
.마다 암묵적으로 일어나는 일"의 문제라기보다, place를 더 일급 시민으로 만들고, 내장이 아닌 스마트 포인터(일급이든 삼급이든)가 자신의 의미 체계를 컴파일러에 더 잘 표현할 수 있게 해서, 모든 포인터를 내장 타입 수준의 편의성으로 끌어올리자는 이야기입니다.현재 방식이 좋다고 하면서 에러가 나는 이유를 그럴듯하게 설명할 수 있다는 점을 근거로 드는 건 이번 변경의 요점을 놓친 겁니다. MutexGuard가 없는 같은 코드도 개념적으로는 똑같은데, 거기서는 가변 참조가 두 번 역참조되어도 에러가 나지 않습니다. 같은 표현력을 쓰려면 컴파일러가 원래 해 주던 가변 참조 강제 변환을 프로그래머가 직접 손으로 써야 한다는 걸 알고 있어야 하는 겁니까? 이게 좋고 직관적인 언어 설계입니까?
그 질문은, 에러가 나는 이유를 짚어낼 수 있다는 점 말고 기사의 예시에서 어떤 부분이 설득력 없다고 보시는지 구체적으로 말씀해 주시면 답하기가 더 쉬울 겁니다.
단지 에러가 나는 이유가 있다는 게 전부가 아닙니다. (1) 참조와 box를 우대하고 있긴 해도 지금은 빌림 검사기가 할 수 있는 것과 없는 것의 경계가 분명하고, (2) 고치는 데 한 줄이면 됩니다. 여기저기서 한 줄씩 아끼자고 트레이트를 통해 언어의 후킹 지점을 더 늘리는 게 설득력 있어 보이지 않는다는 겁니다. 그래서 코드가 훨씬 눈에 띄게 개선되는 예시를 물었던 거고요.
가령 언급된 로드맵 페이지에는 "하지만 Option<&mut T>나 커스텀 스마트 포인터는 어색한 .as_deref_mut()나 .reborrow() 호출이 필요합니다"라고 쓰여 있는데, 제가 묻는 건 그게 얼마나 어색하냐는 겁니다.
이해가 안 되네요, 이게 리눅스랑 무슨 상관이죠?
왜 리눅스 뉴스 블로그에 올라와 있는 거죠?
글쎄요, Rust가 처음 나왔을 때부터 리눅스 커뮤니티 전반의 관심사였기 때문 아닐까요? ( https://lwn.net/Articles/547145/ )
게다가 Rust가 커널에 들어오면서 커널이 자체 추상화와 요구사항을 표현해야 하게 됐고(alloc을 쓸 수 없으니 box도 쓸 수 없고요), "유저랜드" 스마트 포인터의 한계(사용성이든 다른 면이든)가 그 어느 때보다 두드러지고 중요해졌습니다.
LWN.net이 리눅스 커널 이슈만 다루는 곳은 아닙니다. FAQ에는 이렇게 나와 있습니다.
LWN이라는 게 뭔가요?
LWN.net은 자유 소프트웨어 커뮤니티를 위한 최고의 뉴스 및 정보 출처가 되는 것을 목표로 합니다. 개발, 법률, 상업, 보안 문제를 포괄적으로 다룹니다.
https://lwn.net/op/FAQ.lwn
이제 커널에 Rust가 들어와 있잖아요. 리눅스가 쓰는 언어가 관련 있는 건 당연하죠.