Pin은 자기참조(self-referential) 구조체를 나타내기 위해서 만들어진 래퍼 타입이다


일단 Pin없이 자기참조 구조체를 만들어보자





잘 보면 a의 참조를 리턴하는게 아니라 저장해준 참조를 리턴한다 이건 수명으로 나타낼수가 없어서 b는 참조가 될수없고 그냥 포인터를 써야한다


이 자기참조 구조체의 특징은 참조가 가르키는 대상과 참조의 소유권이 동시에 존재하기 때문에 참조를 냅두고 대상이 소멸할 일은 없다는것이다


이것만보면 잘 돌아가는거 같지만 만약 SelfRef가 이동하면 어떻게 될까?





SelfRef를 리턴해서 이동시켜봤다 이때 b는 foo 함수의 스택을 가르키고 있고 당연히 main에서 읽고 쓰기를 시도하는것은 UB다


miri를 돌려보면 자세한 설명이 나온다



이미 freed된곳을 역참조 즉 이미 끝난 함수의 스택을 읽으려 했다는것이다


이처럼 참조가 가르키는 대상이 소멸될일은 없지만 대상을 가르키는 주소는 이동할때 변할수가 있다


Rust에서 이동은 아무런 제약이 없기에 이걸 방지하려면 이동해도 변하지 않는 주소일때만 참조를 저장해야한다


그걸 위해서 존재하는 타입이 바로 Pin이다





이제 이 함수들이 safe하게된 이유를 찾아보자


일단 Pin을 safe하게 만드는 방법은 Pin::new인데 이건 Unpin을 요구한다




이때 필드에 PhantomPinned를 박아놨기 때문에 SelfRef는 !Unpin이다


그러므로 단순히 스택에 있는 값으로 만들려고 하면 컴파일 에러가 발생한다


그래서 위 코드에선 Box::pin을 사용했는데 Box는 항상 Unpin이기에 Box<SelfRef> 또한 Unpin이라서 Pin<Box<SelfRef>>를 만들수있다





그리고 이렇게 만든 Pin<Box<SelfRef>>를 Pin<&mut SelfRef>로 만드는건 safe하다 왜냐면 이미 고정된 주소를 가지고 있기 때문이다


결국 Pin을 둘러쓴 타입은 항상 고정된 주소를 가지게 되고 b도 Pin으로부터 초기화 했기 때문에 안전하게 된다