GN⁺: Rust의 Pin에 대해서 알아보기
(without.boats)Pin
-
Pin
타입과 pinning 개념은 Rust 비동기 생태계의 기초적인 구성 요소임 - 그러나
Pin
은 접근하기 어렵고 오해받기 쉬운 요소 중 하나임 - 이 글은
Pin
이 무엇을 달성하는지, 어떻게 생겨났는지, 그리고 현재Pin
의 문제점이 무엇인지 설명함
Requirements
- 비동기 함수에서 참조를 지원하기 위해
Future
내부에 참조를 저장해야 했음 - 문제는 이러한 참조가 자기 참조 일 수 있다는 것임
- 예시 코드:
async fn foo<'a>(z: &'a mut i32) { ... } async fn bar(x: i32, y: i32) -> i32 { let mut z = x + y; foo(&mut z).await; z }
-
Bar
의 내부 상태는 다음과 같음:enum Bar { Start { x: i32, y: i32 }, FirstAwait { z: i32, foo: Foo<'?> }, Complete, }
-
Pin
의 목표는 안전하게 자기 참조 타입을 조작하는 것임
Non-solutions: move constructors and offset pointers
- 이동 생성자와 오프셋 포인터는 Rust에서 작동하지 않음
- 이동 생성자는 이동 시 포인터를 수정하지만, Rust에서는 불가능함
- 오프셋 포인터는 컴파일 시 참조가 자기 참조인지 아닌지 알 수 없기 때문에 작동하지 않음
The “pinned typestate”
- 객체가 항상 이동 불가능한 것이 아니라 특정 시점부터 이동 불가능해야 함
- Ralf Jung의 모델에서는 객체가 "소유됨" 상태에서 "공유됨" 상태로, 그리고 "고정됨" 상태로 전환됨
- 고정된 상태에 들어가면 객체는 더 이상 이동할 수 없음
?Move
-
Pin
이전에Move
라는 새로운 트레이트를 기반으로 한 솔루션을 시도했음 -
Move
를 구현하지 않는 타입은 참조를 취할 때 고정 상태로 전환됨 - 그러나
Move
는 역호환성을 제공하지 않음
Pin
-
Pin
은 객체를 고정 상태로 만드는 새로운 참조 유형을 설계함 -
Pin
은 라이브러리 API로 구현되어 역호환성을 유지함 -
Unpin
자동 트레이트를 추가하여 대부분의 타입이 고정 상태와 일반 상태를 구분하지 않도록 함
The problems with Pin
-
Pin
은 사용성 측면에서 여러 문제를 가지고 있음 -
Pin
은 라이브러리 타입으로 구현되어 일반 참조 타입이 가진 많은 기능이 사라짐 - 예를 들어,
&mut T
는Copy
를 구현하지 않지만 여러 번 인자로 전달할 수 있음 -
Pin
은 이러한 편의를 제공하지 않음 -
Pin
을 사용할 때 많은 혼란이 발생함
In my next post…
-
Pin
은 비동기 함수에서 임의의 참조를 안전하게 컴파일할 수 있게 해줌 - 그러나
Pin
은 복잡성을 증가시키며, 이를 개선할 방법을 다음 글에서 다룰 예정임
GN⁺의 정리
-
Pin
은 Rust 비동기 생태계의 중요한 구성 요소임 -
Pin
의 사용성 문제는 라이브러리 타입으로 구현되었기 때문임 -
Pin
을 개선하기 위한 방법을 다음 글에서 다룰 예정임 - 비슷한 기능을 가진 프로젝트로는
pin-project-lite
가 있음
Hacker News 의견
-
Pin이 이해하기 어려운 이유는 공식 문서에서 명확하게 설명되지 않기 때문임
- 문서에서 "Pin은 객체가 절대 이동하지 않도록 보장한다"고 주장하지만, 이는 사실이 아님
- 대부분의 일반 객체는 Unpin이므로 Pin은 보통 아무런 역할을 하지 않음
- Pin이 실제로 작동하는 타입 T의 집합은 매우 특이하고 문서에서 충분히 강조되지 않음
-
Pin이 어려운 이유는 자체적으로 의미가 없기 때문임
- Pin<&mut InnerType>의 경우, 언어나 표준 라이브러리에서 Pin이 무엇을 할 수 있고 할 수 없는지 알려주지 않음
- 대신, InnerType 제공자가 추가적인 (내부적으로 안전하지 않은) 메서드와 API를 만들어 핀된 객체를 조작할 수 있게 함
- Pin<P> 자체의 유일한 목적은 더 적은 "내재된 기능"을 제공하는 포인터를 제공하는 것임
-
제목에 "rust"를 추가해야 기사 내용이 무엇인지 알 수 있음
-
"value identity"라는 용어는 Mojo의 문서 어디에서도 정의되지 않았음
- Dave Abrahams의 "Value Semantics: Safety, Independence, Projection, & Future of Programming" 강연을 추천함
-
Pin은 기술적으로 정확하지만 이해하기 어려운 이름의 좋은 예임
- "Drop"은 더 익숙한 의미를 가지지만, "pinning"은 그렇지 않음
- "immovable!(…)"이 더 나을지 모르겠지만, 더 나은 이름을 생각하기 어려움
- "prevent_moving!(…)"과 같은 설명적인 이름과 PreventMove 트레이트가 더 나을 수 있음
-
Rust와 유사한 언어에서 move-constructors가 있다면 Pin의 필요성이 사라질 수 있음
- 사용자가 객체를 파괴할 방법이 없기 때문에 이동할 방법도 없을 것임
-
&mut 참조를 통해 mem::swap/replace로 객체를 이동할 수 있지만, 실제로 필요한 경우는 드묾
- 이동-참조를 선택할 수 있는 방법이 있었으면 좋겠음
- swap과 replace를 안전하지 않게 만드는 것이 문제를 해결할 수 있을 것임
-
WithoutBoats는 비동기 반복자, poll, pin 주제에 대해 활발한 토론을 진행 중임
- 언어의 세부 사항에 대해 공개적으로 깊이 논의하는 커뮤니티가 거의 없으며, 이를 보는 것이 매우 흥미로움
-
Pinning/!Move는 비동기/대기 외에도 많은 용도로 유용함
- 하지만 Rust가 이를 제대로 처리하지 못해, 보통의 대답은 "프로그램을 다른 언어로 다시 작성하라"임