1 ポイント 投稿者 GN⁺ 2024-07-22 | 1件のコメント | WhatsAppで共有

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 TCopy を実装しないが、複数回引数として渡すことはできる
  • Pin にはそのような利便性がない
  • Pin を使うと多くの混乱が生じる

In my next post…

  • Pin は、非同期関数で任意の参照を安全にコンパイルできるようにしてくれる
  • しかし Pin は複雑さを増し、その改善方法については次回の記事で扱う予定である

GN⁺の要約

  • Pin は Rust の非同期エコシステムにおける重要な構成要素である
  • Pin の使い勝手の問題は、ライブラリ型として実装されていることに起因する
  • Pin を改善する方法については次回の記事で扱う予定である
  • 類似の機能を持つプロジェクトとして pin-project-lite がある

1件のコメント

 
GN⁺ 2024-07-22
Hacker Newsの意見
  • Pinが理解しにくいのは、公式ドキュメントで明確に説明されていないため

    • ドキュメントでは「Pinはオブジェクトが決して移動しないことを保証する」と主張しているが、これは事実ではない
    • 一般的なオブジェクトの大半はUnpinなので、Pinはたいてい何の役割も果たさない
    • Pinが実際に機能する型Tの集合は非常に特殊であり、その点がドキュメントで十分に強調されていない
  • Pinが難しいのは、それ自体には意味がないため

    • Pinの場合、言語や標準ライブラリはPinに何ができて何ができないのかを教えてくれない
    • その代わりに、InnerTypeの提供者が追加の(内部的にunsafeな)メソッドやAPIを作り、pinされたオブジェクトを操作できるようにする
    • Pin自体の唯一の目的は、より少ない「組み込み機能」しか持たないポインタを提供すること
  • タイトルに「rust」を追加しないと記事の内容が何なのかわからない

  • 「value identity」という用語はMojoのドキュメントのどこにも定義されていない

    • Dave Abrahamsの「Value Semantics: Safety, Independence, Projection, & Future of Programming」という講演を勧める
  • Pinは、技術的には正確だが理解しにくい名前の良い例

    • 「Drop」はよりなじみのある意味を持つが、「pinning」はそうではない
    • immovable!(…)のほうがよいかもしれないが、より良い名前を考えるのは難しい
    • prevent_moving!(…)のような説明的な名前とPreventMoveトレイトのほうがよいかもしれない
  • Rustに似た言語でmove constructorがあれば、Pinの必要性はなくなるかもしれない

    • ユーザーはオブジェクトを破壊する方法がないため、移動させる方法もないはず
  • &mut参照を通じてmem::swap/replaceでオブジェクトを移動できるが、実際に必要になる場面はまれ

    • move-referenceを選べる方法があればよいのにと思う
    • swapreplaceをunsafeにすれば問題を解決できるかもしれない
  • WithoutBoatsは非同期イテレータ、poll、pinについて活発な議論を続けている

    • 言語の細部について公開の場で深く議論するコミュニティはほとんどなく、それを見るのはとても興味深い
  • Pinning/!Moveはasync/await以外にも多くの用途で有用

    • しかしRustはこれをうまく扱えておらず、たいていの答えは「プログラムを別の言語で書き直せ」になってしまう