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 がある
1件のコメント
Hacker Newsの意見
Pinが理解しにくいのは、公式ドキュメントで明確に説明されていないため
Tの集合は非常に特殊であり、その点がドキュメントで十分に強調されていないPinが難しいのは、それ自体には意味がないため
Pinの場合、言語や標準ライブラリはPinに何ができて何ができないのかを教えてくれないInnerTypeの提供者が追加の(内部的にunsafeな)メソッドやAPIを作り、pinされたオブジェクトを操作できるようにするPin自体の唯一の目的は、より少ない「組み込み機能」しか持たないポインタを提供することタイトルに「rust」を追加しないと記事の内容が何なのかわからない
「value identity」という用語はMojoのドキュメントのどこにも定義されていない
Pinは、技術的には正確だが理解しにくい名前の良い例
immovable!(…)のほうがよいかもしれないが、より良い名前を考えるのは難しいprevent_moving!(…)のような説明的な名前とPreventMoveトレイトのほうがよいかもしれないRustに似た言語でmove constructorがあれば、Pinの必要性はなくなるかもしれない
&mut参照を通じてmem::swap/replaceでオブジェクトを移動できるが、実際に必要になる場面はまれswapとreplaceをunsafeにすれば問題を解決できるかもしれないWithoutBoatsは非同期イテレータ、poll、pinについて活発な議論を続けている
Pinning/!Moveはasync/await以外にも多くの用途で有用