Rust: [安定化]ピンAPI

作成日 2018ĺš´11月07日  Âˇ  213コメント  Âˇ  ソース: rust-lang/rust

@rfcbotfcpマージ
機能名: pin
安定化目標:1.32.0
追跡の問題:#49150
関連するRFC:rust-lang / rfcs#2349

これは、 pinライブラリ機能を安定させ、「ピン留め」を行うための提案です。
安定版で使用可能な固定メモリを操作するためのAPI。

(私はこの提案を包括的な「安定化レポート」として書こうとしました。)

安定化された機能またはAPI

[std|core]::pin::Pin

これにより、 std / coreのpinサブモジュールのPinタイプが安定します。 Pinは
ジェネリック型P基本的な透過​​ラッパー。
ポインタ型になります(たとえば、 Pin<&mut T>とPin<Box<T>>は両方とも
有効な意図された構成)。 Pinラッパーは、「ピン」へのポインターを変更します
それが参照するメモリが所定の位置にあり、ユーザーがオブジェクトを移動するのを防ぎます
その記憶の。

Pinタイプを使用する通常の方法は、いくつかの固定されたバリアントを作成することです。
所有するポインタの種類( Box 、 Rcなど)。 すべてのポインタを所有する標準ライブラリ
これを返すpinnedコンストラクターを提供します。 次に、を操作するには
内部の値、これらのポインタはすべて、 Pin<&T>向かって低下する方法を提供します
およびPin<&mut T> 。 固定されたポインタは参照を取り消すことができ、 &Tを返しますが、できません
安全に可変的にderef:これは安全でないget_mutを使用してのみ可能です
関数。

その結果、ピンを介してデータを変更する人は誰でも、
それらがそのデータから移動することは決してないという不変条件。 これにより、他のコードで
データが移動されることはないと安全に想定し、データに(
例)自己参照。

Pinタイプには、次の安定化されたAPIがあります。

impl<P> Pin<P> where P: Deref, P::Target: Unpin

  • fn new(pointer: P) -> Pin<P>

impl<P> Pin<P> where P: Deref

  • unsafe fn new_unchecked(pointer: P) -> Pin<P>
  • fn as_ref(&self) -> Pin<&P::Target>

impl<P> Pin<P> where P: DerefMut

  • fn as_mut(&mut self) -> Pin<&mut P::Target>
  • fn set(&mut self, P::Target);

impl<'a, T: ?Sized> Pin<&'a T>

  • unsafe fn map_unchecked<U, F: FnOnce(&T) -> &U>(self, f: F) -> Pin<&'a U>
  • fn get_ref(self) -> &'a T

impl<'a, T: ?Sized> Pin<&'a mut T>

  • fn into_ref(self) -> Pin<&'a T>
  • unsafe fn get_unchecked_mut(self) -> &'a mut T
  • unsafe fn map_unchecked_mut<U, F: FnOnce(&mut T) -> &mut U>(self, f: F) -> Pin<&'a mut U>

impl<'a, T: ?Sized> Pin<&'a mut T> where T: Unpin

  • fn get_mut(self) -> &'a mut T

特性の実装

Pinの特性の暗黙のほとんどはかなり腐っています、これらの2つは
その操作:

  • impl<P: Deref> Deref for Pin<P> { type Target = P::Target }
  • impl<P: DerefMut> DerefMut for Pin<P> where P::Target: Unpin { }

std::marker::Unpin

Unpinは、ピン留めの保証をオプトアウトする安全な自動特性です。 の場合
固定されたポインタのターゲットはUnpin実装し、変更可能に安全です
それへの間接参照。 Unpinタイプには、そうでないという保証はありません。
Pinから移動されます。

これにより、固定された参照を処理するのが人間工学的です。
固定されていないものを処理するため、自己参照は含まれていません
参照。 Pinの保証は、次のような特殊なケースタイプにのみ関係します。
自己参照構造:これらの型はUnpin実装していないため、
Pinタイプの制限。

stdでのUnpin注目すべき実装:

  • impl<'a, T: ?Sized> Unpin for &'a T
  • impl<'a, T: ?Sized> Unpin for &'a mut T
  • impl<T: ?Sized> Unpin for Box<T>
  • impl<T: ?Sized> Unpin for Rc<T>
  • impl<T: ?Sized> Unpin for Arc<T>

これらは、ピン留めがポインター間で推移的ではないという概念を成文化しています。 それ
つまり、 Pin<&T>は、 Tで表される実際のメモリブロックのみをピン留めします。
場所。 ユーザーは時折これに混乱し、タイプが
Pin<&mut Box<T>>ように、 Tのデータを所定の位置に固定しますが、固定するのは
固定された参照が実際に参照するメモリ:この場合、 Box 's
ヒープへのポインタである表現。

std::marker::Pinned

Pinnedタイプは、 Unpin実装しないZSTです。 それはあなたがすることを可能にします
安定版でのUnpinの自動実装を抑制します。ここで、 !Unpin
まだ安定していません。

スマートポインターコンストラクター

コンストラクターがstdスマートポインターに追加され、固定された参照が作成されます。

  • Box::pinned(data: T) -> Pin<Box<T>>
  • Rc::pinned(data: T) -> Pin<Rc<T>>
  • Arc::pinned(data: T) -> Pin<Arc<T>>

固定と安全に関する注意事項

過去9か月間、ピン留めAPIは次のように数回繰り返されました。
私たちは彼らの表現力と彼らの健全性を調査しました
保証します。 ここでピン留めAPIが安定したと自信を持って言えます
健全で、人間工学の極大値に十分に近い
表現力; つまり、安定化の準備ができています。

固定の難しい問題の1つは、いつ安全に実行できるかを判断することです。
ピンプロジェクション:つまり、 Pin<P<Target = Foo>>から
Pin<P<Target = Bar>> 、ここでBarはFooフィールドです。 幸いなことに、
ユーザーがそのようなものかどうかを判断するのに役立つ一連のルールを体系化することができました
投影は安全です:

  1. (Foo: Unpin) implies (Bar: Unpin)場合にのみ、プロジェクトを固定するのが安全です。
    つまり、 Foo (含むタイプ)がUnpinであることが決してない場合は、
    Bar (投影されたタイプ)はUnpinはありません。
  2. Foo破壊中にBarが移動されない場合にのみ、安全です。
    Fooにデストラクタがないか、デストラクタが慎重に存在することを意味します
    投影されているフィールドから移動しないことを確認するためにチェックしました。
  3. Foo (含むタイプ)がrepr(packed)でない場合にのみ、安全です。
    これにより、フィールドが移動して再配置されるためです。

さらに、std APIは、オブジェクトをスタックに固定する安全な方法を提供しません。
これは、関数APIを使用して安全に実装する方法がないためです。
ただし、ユーザーは、確実にスタックに固定することができます。
固定された参照を作成した後は、オブジェクトを再度移動しないでください。

crates.ioのpin-utilsクレートには、両方のスタックを支援するマクロが含まれています
ピン留めとピン投影。 スタック固定マクロは、オブジェクトを安全に固定します。
投影用のマクロが存在するのに対し、シャドウイングを含むトリックを使用してスタックします
これは安全ではありませんが、射影定型文を書く必要はありません
他の誤った安全でないコードを導入する可能性があります。

安定化前の実装変更

  • []プレリュードからUnpinをエクスポートし、 pin::Unpin削除して再エクスポートします

原則として、std内の複数の場所から物を再エクスポートすることはありません。
1つは実際の定義のスーパーモジュールです(例:短縮
std::collections::hash_map::HashMapからstd::collections::HashMap )。 このため
理由は、 std::pin::Unpinからのstd::marker::Unpinの再エクスポートは
場所。

同時に、送信や同期などの他の重要なマーカー特性が含まれています
プレリュードで。 したがって、 pinモジュールからUnpinを再エクスポートする代わりに、
プレリュードを入れると、 std::marker::Unpinをインポートする必要がなくなります。
それがpinに入れられたのと同じ理由。

  • []関連する関数をメソッドに変更する

現在、 Pinの関連関数の多くは、メソッド構文を使用していません。
理論的には、これは、refrefable内部メソッドとの競合を回避するためです。 しかしながら、
このルールは一貫して適用されておらず、私たちの経験ではほとんど
物事をより不便にしました。 固定されたポインタは不変のみを実装します
deref、変更不可能なderefまたは値によるderef、derefの機能を制限する
とにかく。 さらに、これらの名前の多くはかなり一意です(例: map_unchecked )
競合する可能性は低いです。

代わりに、 Pinメソッドに適切な優先順位を与えることをお勧めします。 するユーザー
内点法にアクセスする必要がある場合は、UFCSを使用できます。
メソッド構文を使用しなかった場合、Pinメソッドにアクセスするために必要です。

  • [] get_mut_unchecked名前をget_unchecked_mut

現在の順序は、標準ライブラリの他の用途と一致していません。

  • [] impl<P> Unpin for Pin<P>削除します

このimplは、固定解除implの標準的な正当化によって正当化されません。 Pin<P>とP間にポインター方向はありません。 その有用性は、ポインタ自体のimplによってカバーされています。

この先物implは、 P: Unpinバウンドを追加するために変更する必要があります。

  • [] Pinをrepr(transparent)としてマークします

ピンは、同じ表現で、その中のポインターの周りの透明なラッパーである必要があります。

接続された機能とより大きなマイルストーン

ピンAPIは、メモリのセクションを安全に操作するために重要です。
移動されないことが保証されます。 そのメモリ内のオブジェクトがそうでない場合
Unpin実装すると、アドレスが変更されることはありません。 これは
自己参照ジェネレーターと非同期関数を作成します。 結果として、
Pinタイプは標準ライブラリfuture APIに表示され、まもなく登場します
ジェネレーターのAPIにも表示されます(#55704)。

PinタイプとそのAPIを安定させることは、安定させるために必要な前兆です。
Future APIは、それ自体が安定化に必要な前兆です。
async/await構文とfutures 0.3非同期IOエコシステム全体の移動
安定した錆に。

cc @cramertj @RalfJung

T-libs finished-final-comment-period

最も参考になるコメント

先物に関するノミコンの章を書くために誰かがフックにいますか? これがいかに微妙であるかを考えると、それは信じられないほど必要なようです。 特に、例、特にバグのある/悪い実装の例とそれらがどのように不健全であるかが明らかになると思います。

全てのコメント213件

@rfcbotfcpマージ

チームメンバー@withoutboatsはこれをマージすることを提案しました。 次のステップは、タグ付けされた残りのチームメンバーによるレビューです。

  • [x] @Kimundi
  • [] @SimonSapin
  • [x] @alexcrichton
  • [x] @dtolnay
  • [] @sfackler
  • [x] @withoutboats

懸念事項:

  • box-pinnned-vs-box-pinhttps://github.com/rust-lang/rust/issues/55766#issuecomment-443371976)
  • ドキュメントの改善(https://github.com/rust-lang/rust/issues/55766#issuecomment-443367737)
  • ネーミングオブアンピンはhttps://github.com/rust-lang/rust/issues/55766#issuecomment-445074980によって解決されました
  • セルフメソッド(https://github.com/rust-lang/rust/issues/55766#issuecomment-443368794)

レビューアの過半数が承認すると(そして最大2つの承認が未解決になると)、これが最終コメント期間に入ります。 このプロセスのどの時点でも提起されていない大きな問題を見つけた場合は、声を上げてください。

タグ付けされたチームメンバーが私に与えることができるコマンドについては、このドキュメントを参照してください。

@withoutboatsで詳細な記事を書いてくれてありがとう! また、歴史的にPinのさまざまな保証に混乱してきましたが、現在、安全性の保証に関してここで安定化されているAPIについていくつか質問があります。 私はこれらのことを書き留めようと思ったのですが、これを自分の頭の中で整理するのを助けるために。

「 Unpin何ですか?」という壁にぶつかり続けますが、これを書き始めようとしています。 私はそれが何であるか、そしてそれを取り巻くさまざまな保証に少し混乱しています。 Tに対して、またUnpin実装しないことの意味をもう一度お話しいただけますか? また、 Unpinがそれを素朴に実装するための安全な特性である場合、 Pin<T>の危険な保証を簡単に損なうために使用できるように思われますが、私は確かに何かを見逃しています

これが正しければ、自己参照ではない(つまり、ジェネレータではない)すべてのタイプが固定解除されます

これは自己参照だけでなく、 Pinでもサポートできる安定したメモリアドレスの使用例がいくつかあります。 しかし、それらは比較的少なく、その中間です。

Unpinが安全に実装できることを私が理解する方法は、それを実装することによって、あなたが書いた他の安全でないコードに必要な不変条件に違反する可能性があるということです(重要なのは、この安全でないコードを書くことができるのはあなただけであり、外部コードはUnpinを実装しました)。 Unpinを実装したかどうかに関係なく、不健全さを引き起こすPinの安全なAPIでできることは何もありません。 Pinの安全でないAPIの一部を使用することを選択することにより、安全な場合にのみUnpin実装することが保証されます。 これは、上記の「ピン留めと安全性に関する注意事項」セクションのポイント1で説明されています。

うーん、私はまだUnpin本当に理解していません。 私は最初、 Unpinを実装することと推進しないことの意味を理解しようとしています。

まず、どのタイプがUnpin自動的に実装するかを知ることはおそらく役に立ちます! 一般的なポインタタイプ(Arc / Rc / Box / reference)がUnpin実装することは前述しましたが、それだけだと思いますか? これが自動特性である場合、それは、型MyTypeがポインターのみを含む場合、自動的にUnpin実装することを意味しますか? または、他のタイプがUnpin自動的に実装しませんか?

Unpin何を保証するかなどを要約したり述べたりしようとしていますが、そうするのは本当に難しいと感じています。 Unpinを実装することの意味と、 Unpin実装しないことの意味をもう一度繰り返すことで、誰かが手助けできますか?

P::Targetのインラインメンバーから移動できないPin<P>の保証を理解していると思いますが、そうですか?

@alexcrichton質問をありがとう、ピン留めAPIは、それらに焦点を当てているグループに参加していない人にとっては少し混乱する可能性があると確信しています。

まず、Unpinを自動的に実装するタイプを知っておくと役に立ちます。

Unpinは、SendやSyncのような自動特性であるため、ほとんどのタイプで自動的に実装されます。 ジェネレーターと非同期関数のタイプは!Unpinです。 ジェネレーターまたは非同期関数本体を含む可能性のある型(つまり、型パラメーターを持つ型)も、型パラメーターが含まれていない限り、自動的にUnpinはなりません。

ポインタ型の明示的な実装は、型パラメータがそうでない場合でもUnpinを作成することです。 この理由は、このコメントの終わりまでに明らかになることを願っています。

Unpinを実装することの意味と、Unpinを実装しないことの意味をもう一度繰り返すことで、誰かが手助けできますか?

これが、ピン留めAPIの基本的な考え方です。 まず、ポインタ型がP場合、 Pin<P>はPように機能しますが、 P::TargetがUnpin実装しPin関連する2つの基本的な不変の安全でないコードを維持する必要があります。

  • Pin<P>から&mut P::Targetを安全に取得できない場合は、 P::Target移動しないでください。
  • Pin<P>できる場合は、デストラクタが実行されるまで、ポインタが指すデータへの固定されていないポインタを取得できないことを保証する必要があります。

これらすべての意味するところは、 Pin<P>を作成した場合、 Pが指す値は二度と移動しないということです。これは、自己参照構造体と侵入型構造体に必要な保証です。コレクション。 ただし、タイプにUnpinを実装するだけで、この保証をオプトアウトできます。

したがって、型にUnpinを実装すると、その型はPin追加の安全保証をオプトアウトすると言います。これは、それを指すポインターを可変的に逆参照することが可能です。 これは、タイプが安全に使用するために不動である必要はないということを意味します。

Tはポインターの後ろにあるため、 Rc<T>ようなポインター型を移動してもTは移動しません。 同様に、( Pin<Box<Rc<T>> ) Rc<T>ポインターを固定しても、実際にはTは固定されず、その特定のポインターのみが固定されます。 これが、ジェネリックをポインターの後ろに保持するものは、ジェネリックが実装されていない場合でもUnpin実装できる理由です。

また、Unpinがそれを素朴に実装するための安全な特性である場合、Pinの危険な保証を簡単に損なうために使用できるようです。、しかし私は確かに何かが欠けています

これは、ピン留めAPIの最も難しい部分の1つであり、最初は間違っていました。

固定解除とは、「ピンに何かが入れられた後でも、それへの変更可能な参照を取得しても安全である」という意味です。 同じアクセスを提供する今日存在する別の特性があります: Drop 。 つまり、 Dropは安全であるため、 Unpinも安全である必要があることがわかりました。 これはシステム全体を損ないますか? 完全ではありません。

自己参照型を実際に実装するには、安全でないコードが必要になります。実際には、誰もが気にする自己参照型は、コンパイラーが生成するもの、つまりジェネレーターと非同期関数ステートマシンだけです。 これらは、 Unpinを実装しておらず、 Drop実装していないことを明示的に示しているため、これらのタイプの場合、 Pin<&mut T>を取得すると、 UnpinまたはDropを実装していないことがわかっている匿名タイプであるため、実際に可変参照を取得することはありません。

将来のコンビネータのように、これらの匿名型の1つを含む構造体があると、問題が発生します。 Pin<&mut Fuse<Fut>>からPin<&mut Fut>に移動するには、 「ピンプロジェクション」を実行する必要があり

このため、ピンの突出は安全ではありません。 ピン留めの不変条件に違反せずにピン投影を実行するには、安定化の提案に記載したいくつかのことを決して行わないことを保証する必要があります。

したがって、tl; dr: Dropが存在するため、 Unpinは安全である必要があります。 しかし、これは全体を台無しにするわけではありません。ピン投影がunsafeあり、プロジェクトをピン留めしたい人は、不変量のセットを維持する必要があることを意味します。

ジェネレーターと非同期関数ステートマシン。 これらは、Unpinを実装しておらず、Drop実装も持っていないことを明示的に示しています。したがって、これらのタイプの場合、Pin <&mut T>を取得すると、実際には変更可能な参照を取得することはありません。 UnpinまたはDropを実装していないことがわかっている匿名タイプ。

非同期ステートマシンにDrop実装するべきではありませんか? 非同期関数の「スタック」にあるもの(おそらくステートマシンのフィールドに等しい)は、非同期関数が完了するかキャンセルされたときに破棄される必要があります。 それともこれは別の方法で起こりますか?

私は、この文脈で重要なものを推測することかどうかであるimpl Drop for Foo {…}でコードを実行することになる、アイテムが存在する&mut Foo例えば使用することができたmem::replace 「exfiltrate」へと移動Foo値。

これは、 ptr::drop_in_place介して呼び出すことができる「ドロップグルー」と同じではありません。 特定のFooタイプのドロップグルーは、実装されている場合はDrop::drop呼び出し、次に各フィールドのドロップグルーを再帰的に呼び出します。 ただし、これらの再帰呼び出しには&mut Foo含まれることはありません。

また、ジェネレーター(したがって非同期ステートマシン)にはカスタムドロップグルーがありますが、現在の状態に基づいて正しいフィールドセットをドロップするだけであり、ドロップ中にフィールドを移動しないことを約束します。

私が使用する用語(標準はないと思いますが):「ドロップグルー」は、コンパイラが生成するフィールドの再帰的ウォーキングであり、デストラクタを呼び出します。 「ドロップ実装」はDropトレイトの実装であり、「デストラクタ」はドロップ接着剤とドロップ実装の組み合わせです。 ドロップグルーは何も動かないので、ドロップの実装のみを考慮します。

先物に関するノミコンの章を書くために誰かがフックにいますか? これがいかに微妙であるかを考えると、それは信じられないほど必要なようです。 特に、例、特にバグのある/悪い実装の例とそれらがどのように不健全であるかが明らかになると思います。

@Gankro確かに、あなたは私をそのために置くことができます。

みなさん、説明ありがとうございます!

私は個人的に非同期RustとPinAPIに非常に慣れていませんが、ここ数日は少し遊んでいました(https://github.com/rust-lang-nursery/futures-rs/pull/1315-where非同期コードの邪魔なコレクションのピン留めを利用しようとしました)。

実験中に、これらのAPIについていくつか懸念がありました。

  • 安定したメモリアドレスが必要であることを知っていても、ピン留めの概念を完全に理解するのは非常に難しいようです。 私が直面したいくつかの課題:

    • 次のようなエラーメッセージ:

      > impl core::future::future::Future+futures_core::future::FusedFutureでは、トレイトstd::marker::Unpinはstd::marker::Pinnedは実装されていません

      これはかなり再帰的で矛盾しているように聞こえます(ピン留めされているものをピン留めしない必要があるのはなぜですか?)

    • Pinをパラメーターとして受け取るメソッドの可変フィールドにアクセスするにはどうすればよいですか。 2つの安全でないメソッドを使用して必要なことを実行するソリューションに到達するまで、新しい用語( Pinプロジェクション)について学び、 pin-utilsからメソッドをコピーしようとすると、少し検索が必要でした。 。

    • asyncメソッドとselect場合に、実際に自分の将来をどのように使用できますか。 先物スタックにpin_mut!()する必要があることがわかりました。 これは実際にはスタックではないため、混乱を招きます。

    • なぜないdrop()方法は、今再び取る&mut self 、代わりにPin 。

    • 全体として、物事をコンパイルすることを期待して、安全でないピン留め関連のAPIをランダムに使用するという感覚が少しありましたが、これは間違いなく理想的な状況ではありません。 他の人も同じようなことをしようとしているのではないかと思います。

  • この概念はコードベース全体に多く浸透しているため、多くの人がPinとUnpin実際に何であるかを理解する必要があるようです。 これは、いくつかの特別なユースケースの実装の詳細が少し漏れているようです。
  • ピンは寿命にいくらか関係しているようですが、いくらか直交しています。 基本的に、ピンは、 drop()が呼び出されるまで、まったく同じアドレスを持つオブジェクトが再び表示される可能性があることを示しています。 これにより、現在のスコープと'static間のある種の仮想ライフタイムが得られます。 関連しているが、それでも完全に異なる2つの概念があるのは紛らわしいようです。 'pinnedようなものでモデル化できるかどうか疑問に思いました。

コンパイルするときに、これらの問題のほとんどが回避されるC ++についてよく考えました。移動コンストラクターと代入演算子を削除することで、型を移動不可と宣言できます。 タイプが移動できない場合、そのタイプを含むタイプも移動できません。 これにより、プロパティと要件は、一部の呼び出し内でプロパティを転送する代わりに、型階層をネイティブに流れ、コンパイラによってチェックされます(すべてではない、たとえばdrop() )。 これはずっと理解しやすいと思います。 しかし、 Futureは現在、何かがポーリングを開始する前に移動する必要があるため、Rustには適用できない可能性がありますか? しかし一方で、それはその特性を持つ新しい定義、または移動とポーリングフェーズの分離で修正できる可能性があります。

Alex Unpinコメントについて: Unpin意味をゆっくりと理解していますが、理解するのが難しいことに同意します。 別の名前が役立つかもしれませんが、今のところ簡潔な名前を見つけることができません。 ThingsInsideDontBreakIfObjectGetsMoved 、 DoesntRequireStableAddress 、 PinDoesntMatterなどに沿ったもの。

私がまだ完全に理解していないのは、 Pin<&mut self>から&mut selfを取得することが、すべてのタイプで安全であるとは限らない理由です。 Pinがオブジェクト自体のアドレスが安定していることを意味する場合、これはほとんどの一般的なRustタイプに当てはまるはずです。 Pinに統合された別の懸念があるようです。それは、Pin内の自己参照型も壊れないということです。 Pinを持った後にそれらを操作するタイプの場合、常に安全ではありません。 しかし、ほとんどの場合、それらはコンパイラーによって生成されると思います。安全でない、または生のポインターでさえ問題ないはずです。 固定された呼び出しをサブフィールドに転送する必要がある手動で将来のコンビネータの場合、それらをポーリングする前に、フィールドにピンを作成するための安全でない呼び出しが明らかに必要になります。 しかし、その場合、安全性は、まったく問題ではない他のフィールドにアクセスするよりも、実際に重要な場所(ピンはまだ有効ですか?)に関連していることがわかります。

私が持っていたもう一つの考えは、いくつかの制限が適用された場合、より単純なシステムを取得することが可能かどうかです。 たとえば、ピン留めが必要なものは非同期メソッド内でのみ使用でき、将来のコンビネータでは使用できない場合です。 たぶんそれは物事をPinまたはUnpinいずれかに単純化することができます。 select / joinをサポートする多くのコードasync / awaitメソッドがコンビネーターにとって有利である可能性があることを考えると、これは最大の損失ではない可能性があります。 しかし、これが本当に役立つかどうかについては、十分に考えていません。

良い面:「スタック」ボローを使用してasync / awaitコードを記述できることは非常に優れており、非常に必要です。 また、侵入型コレクションなどの他のユースケースにピン留めを使用する機能は、組み込みシステムやカーネルなどのターゲットだけでなく、パフォーマンスにも役立ちます。 だから私はこれに関する解決策を本当に楽しみにしています。

安定化レポートに関するマイナーな問題:

fn get_unchecked_mut(self) -> &'a mut T

これは実際にはunsafe fnだと思いますか?

@ Matthias247 Tがピン留め解除されていない場合、 Pin<P<T>>から&mut Tを安全に取得することはできません。これは、Tをピンから移動するためにmem::swapが発生する可能性があるためです。

私が自分自身に説明するのに苦労していることの1つは、PinがAPIの一部である必要があるなど、Futureが他の特性と根本的に異なる理由です。 つまり、async / awaitにはピン留めが必要なためですが、それは、たとえばイテレータとは異なる、Futuresについて具体的に何かを言っているのでしょうか。

ポーリングは&mut selfを取り、 Pin<P>タイプまたはUnpinであるタイプに対してのみFutureを実装できますか?

Pinをパラメーターとして受け取るメソッドの可変フィールドにアクセスするにはどうすればよいですか。

これは実際、 Pinにいくつかの欠落しているメソッドがあるかどうか疑問に思います。

impl<'a, T: ?Sized> Pin<&'a T> {
    fn map<U: Unpin, F: FnOnce(&T) -> &U>(self, f: F) -> Pin<&'a U>
}

impl<'a, T: ?Sized> Pin<&'a mut T> {
    fn map<U: Unpin, F: FnOnce(&mut T) -> &mut U>(self, f: F) -> Pin<&'a mut U>
}

これらはpin_utils::unsafe_unpinned!マクロで使用できます。

このマクロが安全でないと主張する_理由_を解明しようとしています。 !Unpinで、 Unpinフィールドがある場合、このフィールドに投影するのはなぜ安全ではないのでしょうか。

私がそれが不健全であることがわかる唯一の状況は、カスタム!Unpinタイプを実装し、自己のUnpinフィールドへの生のポインターを取得することです(そして安定したアドレスを持っている/を指していることに依存しています同じインスタンス)、次に同じフィールドに&mutを取得し、それを外部関数に渡します。 これは、 Unpin実装が安全である理由と同じ理由に該当するようですが、 !Unpinフィールドへの生のポインターを取得することで、安全なものの一部を呼び出すことができないようにオプトアウトしています。 API。

前の状況をより安全にするために、通常は構造体のUnpinフィールドがPinned追加するのではなく、実際には!Unpinであるマーキング用に、 Pinned上にラッパーを構築することができます。全体としての構造体へのPinned :

pub struct MustPin<T: Unpin>(T, Pinned);

impl<T: Unpin> MustPin<T> {
    pub const fn new(t: T) -> Self { ... }
    pub fn get(self: Pin<&Self>) -> *const T { ... }
    pub fn get_mut(self: Pin<&mut Self>) -> *mut T { ... }
}

これはすべて、現在のAPIと下位互換性があるように見えます。これにより、 futures-rsコンビネータからいくつかの安全性が失われる可能性があります(たとえば、このような追加のフィールドに安全にアクセスできる!Unpinタイプ(侵入型コレクションなど)を実装するために、このようないくつかのAPIを試して、後で追加することができます。

私は可能性があるためNemo157 @これらのマップ機能は、安全ではないmem::swapに&mut T機能戻す前に私を渡す&mut U 。 (可変のものは、不変のものは安全ではないかもしれません)

編集:pin-utilsマクロも異なります。 unsafe_unpinnedはターゲットタイプがUnpinである必要はなく、単なる「固定されていない投影」、つまり&mut Fieldへの投影です。 !Unpinであっても、プロジェクトをそのフィールドに固定しない限り、安全に使用できます。

私が自分自身に説明するのに苦労していることの1つは、PinがAPIの一部である必要があるなど、Futureが他の特性と根本的に異なる理由です。 つまり、async / awaitにはピン留めが必要なためですが、それは、たとえばイテレータとは異なる、Futuresについて具体的に何かを言っているのでしょうか。

理論的な違いはありませんが、実用的な違いはあります。 1つは、 Iteratorが安定していることです。 しかし、結果として、非常に巧妙なものを見つけない限り、二重間接参照なしで自己参照ジェネレーターに対してforループを実行することはできません。ただし、それがなくても完全に安全であるはずです( forループはジェネレーターを消費し、移動しないため)。

もう1つの重要な実用的な違いは、 IteratorとFuture間のコードパターンがまったく異なることです。 先物の待機ポイントを越えて借りたくないと10分行くことはできません。そのため、先物0.1では、これらのArcいたるところに表示されるため、2つの異なる変数で同じ変数を使用できます。 and_then呼び出し。 しかし、我々はすべての自己参照イテレータを表現できずにかなり遠い得ている、それだけで、ユースケースの重要ではありません。

私は可能性があるため、これらのマップ機能は、安全ではないmem::swapに&mut T関数が返す前に私を渡す&mut U

ああ、くそー、それのその部分を忘れた:しかめっ面:

fn as_mut(&mut self) -> Pin<&mut P::Target> fn into_ref(self)->ピン<& 'a T> `

実際に参照を返すメソッドと混同しないように、これらをas_pinned_mutおよびinto_pinned_refと呼ぶ必要がありますか?

stdでのUnpinの注目すべき実装:

impl<P> Unpin for Pin<P> {}ます。 私たちが使用するタイプの場合、これは効果がなく、少し安全に見えますか?
気にしないでください、あなたはあなたのリストにそれを持っています。 ;)

ドロップ保証

安定化する前にDrop保証を成文化する必要があると思います。そうしないと、手遅れになります。

オブジェクトでdropを呼び出さずに、固定されたオブジェクト( Pin参照のターゲット)のストレージを無効にすることは違法です。

ここでの「無効化」は「割り当て解除」を意味する場合がありますが、「転用」も意味します。 xをOk(foo)からErr(bar)に変更すると、 fooのストレージが無効になります。 。

これにより、たとえば、最初にdrop::<T>呼び出さずに、 Pin<Box<T>> 、 Pin<Rc<T>>またはPin<Arc<T>>割り当てを解除することが違法になります。

Deref 、 DerefMut転用

これがDeref特性を「これはスマートポインタ」を意味するように再利用する方法について、私はまだ少し不安を感じています。 Derefは、「継承」などの他の目的にも使用します。 これはアンチパターンかもしれませんが、それでもかなり一般的に使用されており、率直に言って、それは便利です。 :D

しかし、これは健全性の問題ではないと思います。

Unpin理解する

恥知らずなプラグイン:私はこれについて2つのブログ投稿を書きました。これは、(半)正式な構文を読むことをどれだけ楽しんでいるかによって、役立つかどうかによって異なります。 ;)APIはその後変更されましたが、基本的な考え方は変更されていません。

安全なピン突起

@ Matthias247あなたが直面している問題の1つは、現在、ピン留めを伴う抽象化を構築するには、ほとんどの場合、安全でないコードが必要になることだと思います。 これらの抽象化を使用することは問題ありませんが、たとえば、安全なコードで将来のコンビネータを定義することは機能しません。 これは、「ピンプロジェクション」には、コンパイラの変更によってのみ安全に確認できる制約があるためです。 たとえば、あなたは尋ねます

drop()メソッドがPinではなく&mutselfを受け取るのはなぜですか。

ええと、 drop()は古いです-Rust 1.0から存在しています-なので、変更することはできません。 Pin<&mut Self>だけかかるようにしたいと思います。そうすれば、 Unpinタイプは、現在のように&mut取得できますが、これは下位互換性のない変更です。

将来のコンビネータを安全に作成するには、安全なピンプロジェクションが必要です。そのためには、コンパイラを変更する必要があります。これは、ライブラリでは実行できません。 「このタイプにはDropまたはUnpin実装がない」とアサートする方法が必要になることを除いて、 derive proc_macroでほぼ実行できます。

ピンプロジェクション用のこのような安全なAPIを取得する方法を理解することは努力する価値があると思います。 ただし、安定化をブロックする必要はありません。ここで安定化するAPIは、安全なピンの投影と上位互換性がある必要があります。 ( Unpinは、上記のアサーションを実装するためにlangアイテムになる必要があるかもしれませんが、それほど悪くはないようです。)

固定はありません!移動

これらの問題のほとんどが回避されるC ++についてよく考えました。移動コンストラクターと代入演算子を削除することで、型を移動不可と宣言できます。 タイプが移動できない場合、そのタイプを含むタイプも移動できません。

Rustの移動不可能なタイプを定義する試みがいくつかありました。 それは非常にすぐに複雑になります。

理解しておくべき重要なことの1つは、ピン留めによって移動できないタイプが提供されないことです。 Rustではすべてのタイプが移動可​​能なままです。 Tがある場合は、好きな場所に移動できます。 移動できない型の代わりに、移動できないポインター型を定義するため、新しいカプセル化されたAPIを定義するRustの機能を利用Pin<&mut T> )にあり、ポインタ( T )にはありません。 タイプが「私は今まで動かせない」と言う方法はありません。 しかし、タイプが「私はピン止めてしまった場合は、再び私を移動しないでください」と言うための方法があります。 したがって、私が所有するMyFutureは常に移動可能ですが、 Pin<&mut MyFuture>は、これ以上移動できないMyFutureインスタンスへのポインターです。

これは微妙な点であり、この非常に一般的な誤解を避けるために、ここでドキュメントを具体化するために時間を費やす必要があります。

しかし、自己参照イテレータをまったく表現できずにかなり遠くまで到達しました。ユースケースにとってそれほど重要ではありません。

これは、これまでのところ、すべてのイテレータが型とimpl Iterator for …ブロックを使用して定義されているためです。 2つの反復の間に状態を保持する必要がある場合、イテレータタイプのフィールドに状態を格納し、 &mut self以外に選択肢はありません。

ただし、これが言語にジェネレーターを含める主な動機ではない場合でも、最終的にジェネレーターyield構文を使用して、 forで使用できるものを定義できるようになると非常に便利です。 yield全体で借用します。これは、ジェネレーター-イテレーターにとってもジェネレーター-フューチャーにとっても重要だからです。

( Unpinは、上記のアサーションを実装するためにlangアイテムになる必要があるかもしれませんが、それほど悪くはないようです。)

UnpinとPinは、安全な不動のジェネレーターをサポートするために、すでにlangアイテムである必要があります。

すべての説明に感謝します! 私は@Gankroに同意します。 Pinなどに関するノミコンスタイルの章がここで非常に役立つでしょう。 なぜさまざまな安全な方法が存在するのか、なぜ安全でない方法が安全でないのかなど、多くの開発履歴があるのではないかと思います。

これを理解しやすくするために、各関数が安全である理由、またはunsafe理由を書き留めてみたかったのですが。 上からの理解で、私は以下のすべてを持っていますが、あちこちにいくつかの質問があります。 他の人が私がこれを記入するのを手伝ってくれるなら、それは素晴らしいことです! (または私の考えが間違っている場所を指摘するのに役立ちます)

  • fn new(P) -> Pin<P> where P: Deref, P::Target: Unpin

    • これはPin<P>を構築するための安全な方法であり、
      Pin<P>は通常、 P::Targetがで二度と移動されないことを意味します
      プログラム、 P::TargetはUnpinを実装します。これは「
      Pinはもう保持されません」。その結果、ここでの安全性は、
      すべてが通常どおり進行できるように、維持することが保証されます。
  • unsafe fn new_unchecked(P) -> Pin<P> where P: Deref

    • 前とは異なり、 Pはそうではないため、この関数はunsafeです。
      必ずUnpin実装するため、 Pin<P>は次の保証を維持する必要があります。
      P::Target Pin<P>が作成された後、 P::Targetは二度と移動しません。

    この関数が安全である場合、この保証に違反する簡単な方法は、
    お気に入り:

    fn foo<T>(mut a: T, b: T) {
        Pin::new_unchecked(&mut a); // should mean `a` can never move again
        let a2 = mem::replace(&mut a, b);
        // the address of `a` changed to `a2`'s stack slot
    }
    

    したがって、 Pin<P>実際に意味することを保証するのはユーザー次第です
    P::Targetは建設後に再び移動されることはないので、 unsafeです!

  • fn as_ref(&Pin<P>) -> Pin<&P::Target> where P: Deref

    • Pin<P>与えられると、 P::Targetが決して動かないことが保証されます。 それは
      Pinの契約の一部。 結果として、それは些細なことを意味します
      &P::Target 、 P::Targetへの別の「スマートポインタ」は同じものを提供します
      保証なので、 &Pin<P>は安全にPin<&P::Target>に変換できます。

    これは、 Pin<SmartPointer<T>>からPin<&T>に移動する一般的な方法です。

  • fn as_mut(&mut Pin<P>) -> Pin<&mut P::Target> where P: DerefMut

    • ここでの安全性は、上記のas_refとほぼ同じだと思います。
      変更可能なアクセスを配布しているのではなく、 Pinだけなので、簡単にできるものはありません。
      まだ違反しています。

    質問:「悪意のある」 DerefMut implはどうですか? これは安全な方法です
    &mut P::Targetネイティブにクレートするユーザー提供のDerefMutを呼び出すには、
    おそらくそれもそれを変更することを許可します。 これはどのように安全ですか?

  • fn set(&mut Pin<P>, P::Target); where P: DerefMut

    • 質問: Pin<P> (そして私たちが何も知らないという事実)を考えると
      Unpin )、これはP::Target決して動かないことを保証するべきではありませんか? できれば
      ただし、別のP::Targetで再初期化するのは安全ではないでしょうか?
      それとも、これはどういうわけかデストラクタなどに関連していますか?
  • unsafe fn map_unchecked<U, FnOnce(&T) -> &U>(Pin<&'a T>, f: F) -> Pin<&'a U>

    • これはunsafe関数なので、ここでの主な質問は「なぜこれではないのか」です。
      安全」?これが安全である場合の保証違反の例は次のようになります。

    ..。

    質問::ここでの反例は何ですか? これが安全だった場合、何ですか
    Pinの保証に違反していることを示す例?

  • fn get_ref(Pin<&'a T>) -> &'a T

    • Pin<&T>の保証は、 Tが決して動かないことを意味します。 &T返す
      Tは許可されていないため、支持しながら安全に行う必要があります
      この保証。

    ここでの「たぶん落とし穴」の1つは、内部の可変性です。 Tが
    RefCell<MyType> ? ただし、これはの保証に違反しません
    Pin<&T>保証は、 T全体にのみ適用され、
    内部フィールドMyType 。 内部の可変性は動き回ることができますが
    内部では、それでも基本的に構造全体を背後に移動することはできません
    &参照。

  • fn into_ref(Pin<&'a mut T>) -> Pin<&'a T>

    • Pin<&mut T>は、 Tが移動しないことを意味します。 結果として、それはPin<&T>意味します
      同じ保証を提供します。 この変換ではそれほど問題にはならないはずですが、
      それは主にタイプの切り替えです。
  • unsafe fn get_unchecked_mut(Pin<&'a mut T>) -> &'a mut T

    • Pin<&mut T>は、 Tが移動してはならないことを意味するため、これは簡単にunsafe
      結果にmem::replaceして、 T (安全に)移動できるためです。 ザ・
      unsafeここに " &mut Tを差し上げますが、許可されていません
      T "を移動します。
  • unsafe fn map_unchecked_mut<U, F: FnOnce(&mut T) -> &mut U>(Pin<&'a mut T>, f: F) -> Pin<&'a mut U>

    • ここのunsafeは基本的に少なくとも上記と同じだと思います。
      &mut T安全に配布する(安全でない閉鎖を要求することはできません)
      mem::replace簡単に使用できます。 ここにもおそらく他の危険があります
      予測では、しかしそれは少なくとも安全ではないことは合理的であるように思われます
      そのための。
  • fn get_mut(Pin<&'a mut T>) -> &'a mut T where T: Unpin

    • Unpin実装することにより、タイプは「 Pin<&mut T>には保証がない、それは
      &mut T "のニュータイプラッパーだけです。その結果、
      支持して、私たちは安全に&mut T返すことができます
  • impl<P: Deref> Deref for Pin<P> { type Target = P::Target }

    • これは、 as_ref後にget_refことで安全に実装できるため、
      このimplの安全性は上記から得られます。
  • impl<P: DerefMut> DerefMut for Pin<P> where T::Target: Unpin { }

    • これは、 as_mut後にget_mut続けることで安全に実装できるため、
      このimplの安全性は上記から得られます。
  • impl<T: ?Sized> Unpin for Box<T> (およびその他のポインター関連の実装)

    • 他のアクションが実行されなかった場合、 Box<T>はUnpinトレイトを実装します
      TがUnpin実装した場合のみ。 ここでのこの実装は、
      T明示的にUnpin実装していない場合、 Box<T>はUnpin実装します。

    質問:これが根本的に力を与えていることの例は何ですか?

    たとえば、このimplがなければ、安全なものにはunsafe必要になります。

    存在しなかった。


@ Matthias247ピンから&mutTを安全に取得することはできません

> Tがピン解除されていない場合は、mem :: swapを使用してTをピンから移動できます。これにより、ピン留めの目的が無効になります。

ありがとう! はい、 swapは安全でない方法ではないため、これは明らかに問題があります。 しかし、それは追加することによって修正される可能性がUnpinにバインドswap() ? これまでのすべてのコードはUnpinか、とにかく安全ではないため、これで問題が発生することはありません。

まだ私を最も混乱させていることの1つは、 Pin<T>複数の保証を成文化していることだと思います:Tのアドレスが安定していること、およびその内部状態に関するいくつかの保証(一部では凍結/不変のようなものです)状況だけでなく、実際にはそうではありません)。

安全でないコード/プロジェクションを、さらにPinベースの呼び出しが必要な場所(たとえば、フィールドのpoll )にのみ移動する方が、安全でないプロジェクションを処理するよりも望ましいように思えました。すべての場合。 ただし、これには別の問題があることにも気づきました。可変参照にアクセスできるコードはフィールドを自由に移動でき、 drop()がこのフィールドで安全に呼び出されると、次の理由で破損する可能性があります。別の場所に保存されているアドレス。 そのためには、話し合われたdrop(Pin<T>)オーバーロードが必要になります。

@RalfJung説明ありがとうございます! 私は確かに一般的に危険なことをしようとしたという事実に同意します。したがって、私に追加の理解を要求することは問題ないはずです。 私は、より一般的に安全な将来のコンビネータを書きたいと思っている人々についてもっと心配していますが、今ではそれらすべての用語に直面するかもしれません。 Unpinとピンプロジェクションをまったく理解せずにコンビネータを記述でき、( Unpin先物でのみ)機能が制限されたコンビネータのみを取得できる場合は、それが望ましいと思われます。 私はそれを試したことがないので、それが現在そうであるかどうかは言えません。 少なくともUnpin境界を手動で追加する必要があると思います。

また、動かせないタイプとピンタイプが違うことも理解しています。 しかし、私は現在、違いよりもユースケースに焦点を当てています。 また、煩わしいコレクションのユースケースでは、移動不可能なタイプは、あまり複雑にすることなく非常にうまく機能します。 先物については、移動可能なタイプから移動不可能なタイプへの道が欠けているため、明らかにいくつかの調査が必要になります。 その方法がPinAPIよりも人間工学的でない場合、勝利もありません。

T: Unpinをmem::swap追加すると、ピン内にない場合でも特定のタイプで使用できなくなります。

しかし、swap()にバインドされたUnpinを追加することで修正できますか? これまでのすべてのコードはピン留めを解除するか、とにかく安全ではないため、これで問題が発生することはありません。

それは、すべての汎用コードを破る:いくつかのためにあなたが今日の安定したさびで関数を記述する場合はT制約を受けることなく、あなたが呼び出すことができますswapそれに。 これは機能し続ける必要があります。

移動不可能なタイプは、あまり複雑にすることなく非常にうまく機能します。

ここで安定化のために提案されているものを大幅に超える複雑さなしに、後方互換性のある方法でRusthttps://github.com/rust-lang/rfcs/pull/1858を参照して

https://github.com/rust-lang/rfcs/pull/2349のRFCと、ここから始まるボートのブログシリーズは、検討された他の設計の背景と印象を与えるのに役立つはずです。 (日付にも注意してください、このデザインはほぼ10ヶ月間作業中です!)

また、mem :: swapは、まったく興味深い機能ではないため、赤いニシンです。 文字通りただ

let temp = *a; 
*a = *b; 
*b = temp;

@Gankroは安全でないコードを使用していませんか? Afaikそれを文字通り書くことは不可能です。

編集:これについて別の考え方をすると、 T: Unpinをmem::swap追加すると、実際には言語レベルでの安全性の定義が変わると思います。 それは野生のすべてのmycrate::swap fnsを壊すでしょう。

T:Unpinをmem :: swapに追加すると、特定のタイプがピン内にない場合でも使用できなくなります。

Unpinが自動的に導出される場合( Sync / Sendと同じ方法で)、これは問題ではないと思いました。

それはすべてのジェネリックコードを壊すでしょう:制約なしでいくつかのTのために今日の安定した錆の中で関数を書くなら、あなたはそれに対してスワップを呼び出すことができます。 これは機能し続ける必要があります。

しかし、これは明らかにそうです。 特性の境界をRustで明示的に伝播する必要があるという事実については考えていませんでした。

ここで安定化のために提案されているものを大幅に超える複雑さを伴わずに、下位互換性のある方法で移動不可能なタイプをRustに追加する方法を示した人は誰もいません。 これらの古い提案と議論のいくつかはRFCリポジトリにあります。 一例として、rust-lang / rfcs#1858を参照してください。

おかげで、時間があれば、これに関する前の作業についてもう少し読みます。 明らかに、これにはすでに多くの考えと努力が注がれており、私は確かに物事をブロックしたくありません。 これを処理しようとするときに、懸念事項と質問を提供したかっただけです。

@ Matthias247

Unpinが自動的に導出される場合(Sync / Sendと同じ方法で)、これは問題ではないと思いました。

はっきりしていたかどうかわかりません。 明確にするために、 !Unpinタイプを移動することは完全に安全であり、したがってmem::swapを移動することは完全に安全です。 タイプT: !Unpinを固定した後、つまりPin<P<T>>内に移動するのは安全ではありません。

たとえば、async / awaitコードでは、非同期関数から返された先物を好きなだけ移動できます。 pin_mut!するか、 Pin<Box<..>>>などに入れると、移動できなくなります。

私はこれについていくつかのさまざまな質問があります:

  1. asyncとジェネレーターのPin<T>の重要性と、Rustの他の部分(たとえばswap & replace )と対話するときの不健全さの可能性を考えると、ここで提案されているピン留めAPIのバリアントのフォーマル検証( @jhjourdanまたは

  2. このAPIは、Rustの抽象マシン/操作的セマンティクスに関する新しい保証を提供しますか? つまり、 asyncとawait /ジェネレーターのバッキングメカニズムとしてのユースケースを忘れた場合、これをエコシステムのクレート内に配置できますか?現在の保証があれば、それは機能します。与える?

  3. 固定APIを安定させた結果、どのような種類のAPIまたは型システムの追加が不可能になりますか? (この質問は2の拡張です。)

  4. 安定化されるAPIは、フィールドプロジェクションなどを改善するために言語提供の&pin Tタイプを進化させるという点で、どのような手段を提供しますか(提案されたAPIではあまり良くないようです)。

私はいくつかのさまざまなメモを持っています:

  1. 標準ライブラリのドキュメントはかなりまばらに見えます。 :(

  2. 私は、ピン留め構造が非常に精神的に負担がかかる/複雑であることに同意します。

  3. ドキュメントのUnmovable例は非常に複雑に見え、 unsafeます。 これは最適ではないようです。 言語のRFCドラフトで詳しく説明されているように段階的に初期化すると(つまり、NLLを改善する)、代わりに次のことができます。

struct Unmovable<'a> {
    data: String,
    slice: &'a str,
}

let um: Unmovable<'_>;
um.data = "hello".to_string();
um.slice = &um.data; // OK! we borrow self-referentially.

drop(um); // ERROR! `um.slice` is borrowing `um.data` so you cannot move `um`.

// You won't be able to take a &mut reference to `um` so no `swap` problems.

これには安全でないゼロが含まれ、ユーザーがこれに対処するのは非常に簡単です。

さらに、std APIは、オブジェクトをスタックに固定する安全な方法を提供しません。
これは、関数APIを使用して安全に実装する方法がないためです。

このようなAPIはどうですか?

pub fn using_pin<T, R, F>(value: T, f: F) -> R
where F: for<'a> FnOnce(Pin<&'a mut T>) -> R {
    pin_mut!(value);    // Actual implementation inlines this but the point is this API is safe as long as pin_mut! is safe.
    f(value)
}

私はピン留めAPIの開発をあまり厳密にフォローしていなかったので、これは他の場所で言及または説明されている可能性があり、それを見つけることができませんでした。その場合はお詫びします。

Pinnedタイプは、 Unpin実装しないZSTです。 それはあなたがすることを可能にします
安定版でのUnpinの自動実装を抑制します。ここで、 !Unpin
まだ安定していません。

!Unpin implsを安定させることができなかった理由についての説明はどこかにありますか?

Foo(含むタイプ)がrepr(packed)されていない場合にのみ安全です。
これにより、フィールドが移動して再配置されるためです。

パックとは、フィールドを動的に移動できることを意味しますか? それは少し怖いです。 llvmが他の状況でフィールドを移動するコードを生成しないことは確かですか? 同様に、スタックに固定された値がllvmによって移動される可能性はありますか?

微妙ですが、これは本当に素晴らしいAPIのようです。 よくできました!

@Centril Ralfは、彼のブログにピン留めについて書いています。

pin APIは言語の変更をまったく含まず、既存の言語機能を使用して標準ライブラリに完全に実装されています。 Rust the languageには影響せず、他の言語機能を差し押さえることはありません。

Pinは、Rustの最も価値のある古典的な機能の1つである、API unsafe一部をマークすることによってAPIに不変条件を導入する機能の巧妙な実装にすぎません。 Pin 、1つの操作( DerefMut )を安全でないものにするためにポインターをラップし、それを実行する人々が特定の不変条件を支持することを要求し(参照から移動しないこと)、他のコードがこれは決して起こらないと仮定します。 この同じトリックの同様のはるかに古い例はString 。これにより、UTF8以外のバイトを文字列に入れるのは安全ではなくなり、他のコードはString内のすべてのデータがUTF8。

!Unpin implsを安定させることができなかった理由についての説明はどこかにありますか?

負のimplは現在不安定であり、これらのAPIとはまったく関係がありません。

負のimplは現在不安定です。

🤦‍♂️それは理にかなっています、もう少し長く考えるべきでした。 ありがとう。

@alexcrichtonこれは、このAPIの優れた分析です。失われるコメントよりも、適切な場所に保存するようにしてください。

いくつかのコメント:

as_mut :質問:「悪意のある」DerefMutimplはどうですか? これは安全な方法です
&mut P :: Targetをネイティブに作成するユーザー提供のDerefMutを呼び出すには、
おそらくそれもそれを変更することを許可します。 これはどのように安全ですか?

基本的に、 new_uncheckedを呼び出すと、このタイプのDerefとDerefMut実装について約束します。

set :与えられたピン

(そして私たちが何も知らないという事実
固定解除)、これはP :: Targetが決して動かないことを保証するべきではありませんか? できれば
ただし、別のP :: Targetで再初期化するのは安全ではないでしょうか?
それとも、これはどういうわけかデストラクタなどに関連していますか?

これにより、ポインタの古いコンテンツが削除され、そこに新しいコンテンツが配置されます。 「固定」とは「落とさない」という意味ではなく、「落とされるまで動かない」という意味です。 したがって、 drop呼び出し中にドロップするか、上書きしてdrop呼び出すことは非常に重要です、それは私が上で述べたドロップ保証です。

ここで何かが動いているのはどこにありますか?

map_unchecked :質問::ここでの反例は何ですか? これが安全だった場合、ピンの保証に違反していることを示す例は何ですか?

例としては、 Pin<&&T>で開始し、このメソッドを使用してPin<&T>を取得します。 固定は、参照を「伝播」しません。

get_ref :ここでの「たぶん落とし穴」の1つは、内部の可変性です。
RefCell?

確かに、これは落とし穴ですが、あなたが観察したように、健全性の問題ではありません。 どのような不健全になることから行く方法持っているPin<RefCell<T>>にPin<&[mut] T> 。 基本的に、 RefCellはピン留めを伝播せず、 impl<T> Unpin for RefCell<T>ます。

ここで提案されているピン留めAPIのバリアントについて、フォーマル検証( @jhjourdanや@RalfJungなど)が行われていますか?

いいえ、私たちの側からではありません。 上記のブログ投稿には、これをどのように形式化するかについての考えがいくつか含まれていますが、実際にはそれを実行していません。 タイムマシンや興味のある博士課程の学生を教えていただければ、それを行います。 ;)

async&await / generatorsのバッキングメカニズムとしてのユースケースを忘れた場合、これをエコシステムのクレート内に配置できますか?現在の保証があれば機能しますか?

それが意図です。

固定APIを安定させた結果、どのような種類のAPIまたは型システムの追加が不可能になりますか? (この質問は2の拡張です。)

ええと、自信を持ってその質問に答える方法がわかりません。 可能な追加のスペースは大きすぎて、また高次元なので、私はそれについてどんな種類の普遍的な声明もあえてしません。

安定化されるAPIは、フィールドプロジェクションなどを改善するために言語提供の&pin Tタイプを進化させるという点で、どのような手段を提供しますか(提案されたAPIではあまり良くないようです)。

&pin Tについてはもうわかりませんが、新しい汎用のPin<T>とはあまり一致しません。 予測の処理に関しては、「 UnpinとDropはこのタイプには実装されていません」と言うためのハックが必要です。そうすれば、マクロを使用してこれを安全に行うことができます。 追加の人間工学については、言語の一般的な「フィールドプロジェクション」機能が必要になる可能性があります。これは、 &Cell<(A, B)>から&Cell<A>への移行もカバーします。

!Unpin implsを安定させることができなかった理由についての説明はどこかにありますか?

AFAIKの負の実装には長年の制限があり、一般的な境界を追加すると、思ったとおりに機能しないことがよくあります。 (一般的な境界は、無視される場合があります。)

たぶんChalkはこれをすべて修正し、たぶん一部だけを修正しますが、どちらにしても、Chalkでこれをブロックしたくないでしょう。

パックとは、フィールドを動的に移動できることを意味しますか? それは少し怖いです。

dropが整列された参照を期待していることを考えると、これはパックされたフィールドでdropを呼び出す唯一の適切な方法です。

llvmが他の状況でフィールドを移動するコードを生成しないことは確かですか? 同様に、スタックに固定された値がllvmによって移動される可能性はありますか?

LLVMがバイトをコピーしても、プログラムの動作を変更できないため、問題はありません。 これは、Rustで観察できる方法で、データを「移動する」という高レベルの概念に関するものです(たとえば、データへのポインターがデータを指していないため)。 LLVMは、ポインタがある可能性のある他の場所にデータを移動することはできません。 同様に、LLVMは、アドレスが取得されたスタック上の値を移動することはできません。

@RalfJungに感謝し

「ドロップされるまで移動しない」と言う場合、これは、 &mut selfがPin<&mut Self>アドレスから移動した場所でDropが呼び出される可能性があることを意味しますか? デストラクタは、内部ポインタが正確であることに依存することはできませんよね?

setを見るときの私の心配は、パニックがどのように処理されるか

「ドロップされるまで移動しない」と言う場合、これは、 &mut selfがPin<&mut Self>アドレスから移動した場所でDropが呼び出される可能性があることを意味しますか? デストラクタは、内部ポインタが正確であることに依存することはできませんよね?

@alexcrichton私の理解では、 Drop::dropが戻るまで動かないということです。 そうしないと、一部のユースケース(侵入型コレクションと少なくともスタックに割り当てられたDMAバッファー)が不可能になります。

デストラクタは、以前にPinにあったことを証明できれば、移動されたことのないものに依存できます。 たとえば、ステートマシンが、ピン留めが必要なAPIを介してのみ状態に入ることができる場合、デストラクタは、その状態にある場合、ドロップされる前にピン留めされたと見なすことができます。

コードでは、非ローカルのデストラクタがメンバーを移動しないと想定することはできませんが、自分が作成しているので、自分のデストラクタが物事を移動しないと想定することはできます。

「ドロップされるまで移動しない」と言う場合、これは、&mutselfがPin <&mut Self>のアドレスから移動した場所でDropが呼び出される可能性があることを意味しますか? デストラクタは、内部ポインタが正確であることに依存することはできませんよね?

つまり、 dropが呼び出されるまで、データは(割り当て解除されないなど、他の場所に移動しないという意味で)移動することはありません。 はい、 dropは、そのタイプで表現できない場合でも、固定された場所での実行に依存できます。 dropは(すべてのタイプで) Pin<&mut self>かかるはずですが、残念ながら、それには遅すぎます。

dropが呼び出された後、データは無意味なバイトになります。新しいものをそこに入れたり、割り当てを解除したり、何でもできます。

これにより、たとえば、要素のデストラクタが隣接するポインタを調整することによって要素の登録を解除する、侵入的なリンクリストが可能になります。 そのデストラクタが呼び出されない限り、メモリが消えることはありません。 (要素は引き続きリークされる可能性

私はPin 、 Unpinで見つけることができるすべてと、それに至るまでの議論を読んでいます。私は今何が起こっているのかを理解していると思いますが、たくさんあります。さまざまなタイプの意味、および実装者とユーザーが従わなければならない契約に関する微妙な点。 残念ながら、 std::pinのドキュメント、または特にPinとUnpinについては、比較的ほとんど議論されていません。 特に、私はこれらのコメントからのコンテンツのいくつかを見たいと思います:

ドキュメントに組み込まれています。 具体的には:

  • そのPinは、「1レベルの深さ」のみを保護します。
  • そのPin::new_uncheckedは、 DerefとDerefMutぎimplに制限を課します。
  • PinとDrop間の相互作用。
  • !Unpinは、 Pin配置された後にのみ移動できません。
  • Pin<Box<T>>: Unpin where T: !Unpin理由。 これは上記の「1レベルの深さ」の制限に関連していますが、適切な説明を含むこの具体的な例は、読者に役立つと思います。

@alexcrichtonの反例も非常に役に立ちました。 単なる散文以外のさまざまなunsafeメソッドで何がうまくいかないかを読者に理解させることは、大いに役立つと思います(少なくとも私にとっては確かに役に立ちました)。 一般に、このAPIの微妙さのために、さまざまな安全でないメソッドの安全セクションが拡張され、おそらくstd::pinモジュールレベルのドキュメントからも参照されることを望んでいます。

私自身はまだこのAPIをあまり使っていないので、今は良い状態だという技術的な判断を信じています。 ただし、 Pin 、 Pinned 、 Unpinという名前は、非常に異なるタイプ/特性と比較的複雑なAPIに対して類似していて表現力がないため、理解が難しくなると思います。 (このスレッドに関するいくつかのコメントからも明らかです)。

それらはマーカー特性の命名規則に従っているように見えるので、私は本当に文句を言うことはできませんが、冗長性と自己説明的な名前の間でトレードオフを行うことができるかどうか疑問に思います。

  • Pinned - Pinと同じ単語であるため、混乱を招きます。 PhantomDataように使用されて、そうでなければ欠落しているメタ情報で構造体を補強することを考えると、 PinnedData 、 PhantomPinned 、 PhantomSelfRef 、さらにはDisableUnpinはどうでしょうか。
  • Unpin - https: //github.com/rust-lang/rust/issues/55766#issuecomment -437266922のように、 Unpinという名前で混乱しています。 「複数のあいまいな方法で理解することができます。 IgnorePin 、 PinNeutralでしょうか?

一般的に私は失敗し、自分で良い代替名を見つけています...

PhantomPinとPinNeutralは、特に素晴らしい名前として私を襲います。

対位法を提供するために、私はUnpin直感的であることがわかりました(一度理解すると)。 Pinnedは、自分のコードで使用したことがないので、まっすぐに保つのが難しいです。 PinnedをNotUnpinどうですか?

私は、ピン留めについて話しやすくするために、より良い名前を見つけることが、自転車を流す価値のある演習になる可能性があることに同意します。 私が提案する:

  • Pin -> Pinned : Pinが与えられたとき、それはあなたが与えられたものが_固定されて_、永久に_固定されたままになるという約束です。 しかし、あなたは「価値のピン」を与えられることについても話すことができるので、私はこれについてあまり強く感じません。 Pinned<Box<T>> 、特にBoxが固定されており、含まれているものは固定されていないという点で、私には読みやすくなっています。
  • Unpin -> Repin :他のマーカー特性については、通常、そのマーカー特性を持つもので何ができるかについて話します。 そもそもUnpinが選ばれたのはそのためでしょう。 ただし、ここで読者に本当に取り上げてもらいたいのは、 Unpinものを固定してから、結果や考慮なしに別の場所に再固定できることだと思います。 @ mark-imによるPinNeutralの提案も気に入っていますが、多少冗長です。
  • Pinned -> PermanentPin : Pinnedを含むものは実際には固定されていないため、 Pinnedは適切な名前ではないと思います。 Unpinはありません。 PhantomPinにも同様の問題があり、 Pinが実際に取得したいものではない場合に、 Pin Pinします。 NotUnpinには二重否定があるため、推論が困難です。 @KimundiぎPhantomSelfRefの推測はかなり近くなりますが、それでも少し「複雑」だと思います。また、「一度固定すると移動できない」というプロパティを、その場合の1つぎPermanentlyPinnedと表現することもできます。 どちらの形がそれほど悪くないのかわかりません。

私はと思いますPinnedになってしまうはずですNotXところX何であるUnpinという名前されてしまいます。 Pinnedの唯一の仕事は、囲んでいる型がUnpin実装しないようにすることです。 (編集: Unpinを別のものに変更する場合、おそらく二重否定は問題ではありません)

Repinは私には意味がありません。「このタイプは別の場所に固定できる」というのは、「このタイプはピンから移動できる」という副作用にすぎないからです。

@tikueある意味では同じように感じますが、逆です。 Unpinは負の「これはPinによって制約されていない」と表現されるべきだと思いますが、 Pinnedは正の「これはPinによって制約されている」と表現されるべきですs/Unpin/TemporaryPin 、 s/Pinned/PermanentPin ?

編集:ええ、 RepinがUnpin副作用であるというあなたの指摘がわかります。 Pinは、 Unpinタイプでは「重要ではない」という事実を伝えたかったのですが、 Unpinはあまりうまく機能しないと思います。 したがって、上記のTemporaryPinの提案。

@jonhoo私が反対を好む主な理由は、 Pinnedが特性の実装を妨げるためだと思います。つまり、私にとって最もわかりやすい意味で、真のネガティブです。

編集:どうですか:

Unpin -> Escape
Pinned -> NoEscape

興味深い..私はそれがドキュメントにどのように適合するかを見ようとしています。 何かのようなもの:

一般に、 Pin<P>が与えられると、 Pのターゲットがドロップされるまで移動しないことが保証されます。 これの例外は、 PのターゲットがEscapeです。 Escapeマークされたタイプは、移動されても有効であることが約束されているため(たとえば、内部自己参照が含まれていない)、 Pinを「エスケープ」できます。 Escapeは自動特性であるため、 Escapeであるタイプのみで構成されるすべてのタイプは、それ自体もEscapeです。 実装者は、 impl !Escape for T {}を使用するか、 std::phantomからNoEscapeマーカーを含めることにより、毎晩これをオプトアウトできます。

「エスケープ」という言葉とのつながりは少し希薄に見えますが、それはかなりまともなようです。 これとは別に、上記を書くと、 Pin<P>は、 Pのターゲットが移動しないことを_本当に_保証しないことにも気づきました(正確にはUnpin )。 代わりに、 Pのターゲットが移動するかどうか、またはPのターゲットが移動しないかどうかは関係ありません。 しかし、それを使用して名前のより良い選択を通知する方法がわかりません...しかし、それはおそらく何らかの方法でドキュメントに組み込まれるべきものです。

個人的には、私も名前としてUnpin大嫌いです。おそらく、「ピンを外さない」と読み、いくつかの脳サイクルを必要とするimpl !Unpinと見なされることが最も多いためです(I 'は古いモデルです)「これは、最初に固定すると永久に固定されます」という意味であるため、二重否定を最適化することすらできません。
一般に、人間はポジティブではなくネガティブで考えるのに苦労する傾向があります(直接の情報源はありませんが、疑問がある場合はリチャードハドソンの作品をチェックしてください)。
ところでRepinは私には本当にいいですね。

Pinは、固定された値を常に動かせないようにするわけではないため、説明するのは困難です。 Pinnedは実際には何も固定しないため、混乱を招きます。 Pinエスケープを防ぐだけです。

Pin<P<T>>は、ピン留めされた値として説明できます。ピン留めされた値は、値がピンをエスケープできるタイプでない限り移動できません。

いくつかの簡単なグーグルは、レスリングで、ピンで留められたときに、ピンから抜け出すことをエスケープと

Unpin代わりにエスケープという用語も好きですが、 EscapePinます。

ここにはたくさんの良い考えがあります!

一般的なことの1つ:非ネイティブスピーカーとしての私にとって、 PinとUnpinは主に動詞/アクションです。 Pinは理にかなっていますが、オブジェクトは一度に1つのメモリ位置に固定されているため、 Unpinでは同じものを見ることができません。 Pin<&mut T>参照を受け取ると、Tは、 Unpinであるかどうかに関係なく、メモリ位置が安定しているという意味で常に固定されます。 オブジェクトをアクションとして実際に固定解除することはできません。 違いは、 Unpinタイプでは、それ以降の対話でピン留めの保証が維持される必要がないことです。 それらは自己参照的ではなく、それらのメモリアドレスは、たとえば別のオブジェクトに送信されてそこに格納されることはありません。

私は@tikueに同意しUnpin実際に何を意味するのかについて印象的な作品があればいいのですが、定量化するのは難しいです。 それらのタイプが移動可​​能であるということだけではなく、自己参照性の欠如でもありませんか? 「オブジェクトを移動しても、メモリ空間全体のポインタが無効にならない」ということかもしれません。 次に、 StableOnMoveAfterPinようなもの、または単にStableMoveようなものがオプションかもしれませんが、それらもあまり良くないように聞こえます。

私にとってRepinは、 Unpinと同じ複雑さを持っています。つまり、最初に1つのことが固定解除されることを意味します。これは、私の理解では起こりません。

特性は主に、そのタイプのPinを見た後に何が起こるかを定義するので、 PinNeutralやPinInvariantはそれほど悪くないことがわかります。

PinとPinned 、 Pinnedを選択したほうがいいと思います。これは、それを表示した時点でのポインターの状態だからです。

@ Matthias247 P: Unpin場合、 P::Targetのアドレスが安定していることが保証されているとは思いませんか? 私はこれについて間違っている可能性がありますか?

@ Matthias247

Pin <&mut T>の参照を受け取ると、Unpinであるかどうかに関係なく、メモリ位置が安定しているという意味でTは常に固定されます。

これが何を意味するのか明確にできますか? T: Unpinが与えられると、次のように書くことができます。

let pin_t: Pin<&mut T> = ...
let mut other_t: T = ...
mem::replace(Pin::get_mut(pin_t), &mut other_t);
// Now the value originally behind pin_t is in other_t

@jonhoo実際には良い質問です。 私の推論は、先物がボックス化され、それらのpoll()メソッドが同じメモリアドレスで呼び出されるというものUnpinときに先物を移動させる可能性があります。 だからあなたは正しいようです。

最新のバイクシェディングについて:

  • Unpin : MoveFromPinどうですか? 私がまだいくつかの微妙な点を見逃していない場合、これは特性が実際にどの機能を有効にするかを直接示していると思います。タイプがPinにある場合でも、移動できます。

    重要なのは、 Unpinと同じことを言うのですが、正のアサーションとしてフレーム化されているため、二重否定の!Unpin代わりに!MoveFromPinます。 少なくとも、解釈しやすいと思います...ええと、ピンから移動できないタイプです。

    (基本的な考え方にはバリエーションの余地があります: MoveOutOfPin 、 MoveFromPinned 、 MoveWhenPinnedなど。)

  • Pinned :これはNoMoveFromPinになる可能性があり、その効果はタイプ!MoveFromPinです。 それは簡単に思えます。

  • Pin自体:これは他の2つとは関係がなく、それほど重要ではありませんが、ここでも若干の改善の余地があると思います。

    問題は、(たとえば) Pin<&mut T>は、 &mutが固定されていることを意味するのではなく、 Tが固定されていることを意味します(私が見たと思う混乱)少なくとも1つの最近のコメント証拠)。 Pin部分は、 &mut一種の修飾子として機能するため、 Pinningと呼ぶ方がよい場合があると思います。

    これにはいくつかの間接的な前例があります。パニックの代わりにラップアラウンドするように整数型のオーバーフローセマンティクスを変更する場合は、 Wrap<i32>ではなくWrapping<i32>と言います。

これらはすべてオリジナルよりも長いですが、これらの周りの理由のいくつかがどれほど繊細であるかを考えると、より明確にするために投資する価値があるかもしれません。

Re: Repin 、これは次のようなものになると思います

unsafe trait Repin {
    unsafe fn repin(from: *mut Self, to: *mut Self);
}

これは、コンテンツを時々移動するVecようなコレクション内の!Unpinタイプをサポートするために使用できます(これは、現在またはこれまでにそのような特性を追加するための提案ではなく、特性名)。

また、名前をbikshedding:

  • Pin<P> -> Pinned<P> :ポインタP指す値は、その存続期間中(ドロップされるまで)メモリに固定されます。
  • Unpin -> Moveable :値を固定する必要はなく、自由に移動できます。
  • Pinned (構造体)-> Unmoveable : Pinnedである必要があり、移動できません。

PinまたはUnpinを変更する必要はないと思います。代替案はすべて、私の意見では明確にならずに冗長性を追加するか、まったく誤解を招く可能性さえあります。 また、私たちはすでにこの会話をしていて、 PinとUnpinを使用するという決定に達しました、そしてこのスレッドで提起された議論はどれも新しいものではありません。

ただし、前の説明からPinnedが追加されたので、 PhantomDataようなファントムマーカータイプを明確にするためにPhantomPinnedにするのは理にかなっていると思います。

個人的には、名前としてのUnpinが大嫌いです。おそらく、「not-un-pin」と表示され、結論を出すのに数回のブレインサイクル(私は古いモデルです)を必要とするimpl!Unpinと見なされることが多いためです。つまり、「これは、最初に固定されると永久に固定される」という意味なので、二重否定を最適化することすらできません。

これは私の経験とは完全に反対です。手動で!Unpinを実装するということは、非常にニッチなユースケースである安全でないコードを使用して手動で自己参照構造を実装することを意味します。 対照的に、ポインターの背後に潜在的に固定解除された構造体を保持するものはすべて、 Unpin正の暗黙を持ちます。 implsのすべてのUnpinでstd 、たとえば、正極性です。

@withoutboatsここで提起された議論がすでに議論されているこれらの名前に関する以前の議論へのリンクを提供できますか?

これは1つのスレッドですが、RFCスレッドと追跡の問題についても議論されていますhttps://internals.rust-lang.org/t/naming-pin-anchor-move/6864

(このスレッドのアンカーとピンと呼ばれるタイプは、 Pin<Box<T>>とPin<&'a mut T>と呼ばれるようになりました)

UnpinはUnpinnableの略語として読まれるべきですか? ピン内にある場合でも、実際には値をピン留めしておくことはできません。 (それは私の側でも正しい理解ですか?)

いくつかのドキュメントとコメントスレッドを調べましたが、特にUnpinnableへの参照は見つかりませんでした。

固定解除は、何に対しても短いものではありません。 多くのユーザーには明らかではないと思いますが、本当のことの1つは、標準ライブラリスタイルガイドでは、形容詞ではなく、特性名として動詞を優先することです。したがって、 SendableではなくSend 。 Sendable 。 これは完全な一貫性で適用されていませんが、それは標準です。 Unpinは、「固定を解除する」の場合と同じです。これは、固定したPinからこのタイプの固定を解除できるためです。

Move (「移動可能」ではないことを覚えておいてください)のような名前は、動作をに接続するのではなく、移動できることに関係していることを意味するため、 Unpinよりも明確ではありません。ピンタイプ。 Rustでは任意のSized値を移動できるため、 !Unpinタイプを移動できます。

私が示唆したようにフレーズ全体である名前は、stdにとって非常に単調です。

それは短いことではないかもしれませんが、それはまさに私がそれを読んだ方法です。 特性は動詞であるため、型の操作ではなく型を説明するために使用する場合は、手動で形容詞に変換する必要があります。 std::iter::Iteratorは反復可能なものによって実装され、 std::io::Seekはシーク可能なものによって実装され、 std::pin::Unpinは固定できないものによって実装されます。

@withoutboats動詞の問題がない他の名前、たとえばEscapeやEscapePinに関する特定の問題はありますか? この議論は以前に行われたことを理解していましたが、おそらく今はもっと多くの注目が集まっているので、完全に冗長な再ハッシュであるかどうかはわかりません...

私は本当だと思うことの一つは、その不幸なことということであるPinとUnpinするとき、(いくつかの種類が「ピン」であり、一部には「ピン解除」されている)のペアとして読み取ることができるPinは、動詞ではなく名詞であると想定されています。 Pinが特性ではないという事実は、うまくいけば物事を明らかにします。 Pinningを支持する議論は理にかなっていますが、ここでは名前の長さの問題に直面します。 特に、メソッドレシーバーはself 2回繰り返す必要があるため、多くの文字が表示されます: self: Pinning<&mut Self> 。 Pinning<P>がPin<P>超える4文字全体の明確さであるとは確信していません。

@tikue 「エスケープ」という用語は、ピン留めよりもはるかに過負荷であり、エスケープ分析などの概念と矛盾しています。

また、私たちはすでにこの会話をしていて、ピンとピンを外すという決定に達しました、そしてこのスレッドで提起された議論はどれも新しいものではありません。

これは私を間違った方法でこすります-コミュニティの経験報告は望ましくありませんか? 個人的には、このスレッドの他のコメントのいくつかと、 Pinned二重否定との並置まで、 Unpin明確な問題は見られませんでした。

Rustはエスケープ分析を実行しないので、それが本当の問題であるかどうかはわかりません。

:bell:上記のこれは現在、最終コメント期間に入っています。 :ベル:

これが着陸する前に、https://github.com/rust-lang/rust/issues/55766#issuecomment-438316891で概説されているドキュメントの改善を引き続き確認したいと思います:)

https://github.com/rust-lang/rust/pull/55992を開いて、上記のドキュメントを追加し、 Pinned名前をPhantomPinned 。

Pinned (およびPhantomPinned )は、「固定された」値をPinから移動できない値として概念化することを推奨すると思います。つまり、 Pin内の多くの値を意味します。 Unpinを意味するもの)は「固定」されて

それは紛らわしいようです。 私は、それが簡単にすべての値を概念化するために見つけるPin固定として、彼らはにいる間Pin事は以前の名前何をされ、かつ固定されているかどうかは、永久的であるPinnedコントロール。 Pin*とは別の名前を使用すると、2つの異なる概念の混同を防ぐことができます。

PhantomNotUnpin :P

個人的には、名前としてのUnpinが大嫌いです。おそらく、「not-un-pin」と表示され、いくつかのブレインサイクルを必要とするimpl!Unpinと見なされることが多いためです。

ありがとう! また、ボットが理由を正確に特定できなかったため、かなり長い間Unpinに悩まされてきました。 今、私は理解していると思います:それは二重否定です。

これは私の経験とは正反対で、手動で実装します。!Unpinは、非常にニッチなユースケースである安全でないコードを使用して手動で自己参照構造を実装していることを意味します。 対照的に、潜在的にピンを外す構造体をポインターの後ろに保持するものはすべて、ピンを外すという正の意味を持ちます。 たとえば、stdのUnpinの実装はすべて正極性です。

しかし、それは実装だけでなく、議論についてもです。 impl !Syncはかなりまれですが(不安定なため単独ではありません)、 Syncおよび!Syncタイプについて話すことは非常に一般的です。 同様に、 !Unpinは、少なくとも私が持っていたこの機能の議論でかなり多く出てきました。

私も、プロパティを積極的に表現するもの( MoveFromPin程度)を好みます。 Pinとは異なり、この特性をそれほど頻繁に記述する必要がないため、人間工学に完全に納得しているわけではありません。

Rustはエスケープ分析を実行しないので、それが本当の問題であるかどうかはわかりません。

LLVMはそうするので、エスケープ分析は依然としてRustに非常に関連しています。

それを解決する方法は、 Pin / Unpinの意味を逆にする単語を選ぶことです。 たとえば、 Unpin名前をRelocateます。 次に、 !Unpinは!Relocateます。 それは私にとってはるかに直感的な意味があります-私はそれを「ああ、このタイプのオブジェクトは再配置できない」と読みました。 別の候補はMovableです。

Pin置き換えることができる反対の言葉が何であるか、または必要であるかどうかはわかりません。 しかし、私は確かにドキュメントが次のようなことを言っていることを想像することができました:

固定されたオブジェクトは、オブジェクトをメモリに再配置できる場合に限り、 DerefMutを介して直接変更できます。 Relocateは自動特性であり、デフォルトで追加されます。 ただし、値を保持するメモリへの直接ポインタがある場合は、タイプにimpl !Relocateを追加して、 Relocateオプトアウトしてください。

impl<T: Relocate> DerefMut for Pin<T> { ... }

これは、 Unpinよりもはるかに直感的に理解できます。

上記のドキュメントを追加するために#55992を開きました

ただし、これはhttps://github.com/rust-lang/rust/issues/55766#issuecomment-438316891で提案されたものの一部を追加するだけです。

私はMoveFromPin提案が好きです。 Relocateも良いですが、おそらくPin十分に関連付けられていません。 再びそれを動かせないタイプとして理解することができました(そうではありません)。 RelocateFromPinも良いでしょう。

Escape ingは、たとえばSwiftでクロージャに関連付けられているものであり、現在のコールチェーンの内側または外側のどちらで呼び出されているかを示します。 それは誤解を招くように聞こえます。

わかりやすくする限り、長い名前で問題は発生しません。

FWIW投票を行い、 Unpin名前をRelocateやMoveFromPinなどの「ポジティブ」な名前に変更したいと思います(またはさらに冗長ですが、おそらく少し正確なMayMoveFromPin )。

私はの二重否定ことに同意!UnpinまたはちょうどUnpin正で囲まれ、歴史的に私に混乱してきた、と何かが「これは内部であるにも関わらず移動することができますPinと思います」混乱を和らげるのに役立ちます!

FWIW最初はUnpinについて同じことを考えていましたが、実際にIMOを使用するようになったとき、それは理にかなっています。探している操作はUnpin機能であるため、実際には二重否定ではありません。 Pinに何かを自由に出し入れする)ではなく、物事をピンで留める機能ではありません。 MoveFromPinと同じですが、言い方が異なります。 「 Pinではない」などと思わせない名前がいいのですが、IMO MoveFromPinなどは言葉が多すぎます。 UndoPin ? (ハスケルの群衆のためにFreePin ?)

私はまだ!Unpin奇妙に読めると思います-私はそれを「これを固定解除しないでください!」のように扱うように私の内なる声を訓練しました。 通常の「 Unpin実装していません」の代わりに、少し手間がかかります。

!Pluckどうですか?

@runiq

通常の「固定解除を実装しない」の代わりに

以前のコメントでこれについて言及しましたが、これは実際には良い言い方だと思います。 Unpinは、 Pin<C<_>>を出し入れする操作です。 Unpin実装していないものは、その機能を提供しません。

@cramertj私はUndoPinとても好きです

@cramertj私はこれまでに提案された代替案の大ファンではないことに同意しますが、個人的にはMoveFromPinよりもUnpin MoveFromPinという言葉を好むでしょう。 それがダブルネガティブではないのは良い点ですが、それを読んでいると(まだ1トンも使っていない人として)、ダブルネガティブだと私をつまずかせ続けます。 「Un」プレフィックスを負として読み込もうとしています...

私は興味がありますが、 @ cramertjまたは他の人は、人間工学的にバインドされたUnpinどれだけ発生するかについて、適切なハンドルがあると思いますか? 超レアですか? それは超一般的ですか? 入力するのが苦痛だとしたら、苦痛になるほど一般的ですか?

短くて甘い名前の場合、私は個人的にRelocateアイデアが好きですが、長くて言葉の多い名前の場合は、タイプしないので大丈夫です。 MoveFromPin好きです。 Unpinよりも優れていると感じています。

私は興味がありますが、 @ cramertjまたは他の人は、人間工学的にバインドされた

私の経験では、 Unpin実際にユーザーコードに表示される2つのケースがあります。

  1. Unpin境界が発生するのは、ポーリングするときに実際に未来を移動する必要があるためです(たとえば、一部の選択されたAPIなど)。
  2. より一般的には、futureがfuture以外のものに対してのみジェネリックである場合、通常はUnpin無条件の実装を追加する必要があります。これは、他のタイプがUnpinかどうかは関係ないためです。それらにプロジェクトを固定します。

2番目のケースの例は、ある種のバッファタイプyoure generic over( T: AsRef<[u8]> )がある場合です。 スライスを取り出すためにピン留めする必要はないので、 Unpin実装するかどうかは気にしないので、タイプが無条件にUnpin実装すると言いたいので、実装できます。 Futureピン留めを無視します。

Unpinは、境界と見なされるのがかなり一般的です。 select! 、 StreamExt::next 、およびその他のコンビネータはすべて、操作する型がUnpinます。

@withoutboatsそこにあるあなたのポイント2に興味があります。 impl Unpinは、人々が頻繁に実装することを覚えておく必要があるものだと思いますか? 今日の図書館の作者がカスタムタイプの#[derive(Debug)]またはimpl std::error::Errorを忘れることが多いのと同様に、これらの図書館を使用するのが難しくなっていますか?

固定解除は自動特性です。 unpinを実装しない型があるのは、その型が明示的にオプトアウトするときだけです。 (または、固定解除をオプトアウトするフィールドが含まれています)。

私はそれが事実であることを理解しています。 そして、それが最も一般的なケースであると思いました。そのため、 @ withoutboatsが2番目のポイントについても言及していることに驚きました。 それは私が当初考えていたよりも一般的かもしれないことを私に示唆しているので(おそらくあなた自身の未来を実装するときだけですが)、私はそのような種類のユースケースの頻度に興味があります:)

@alexcrichton興味がありますが、 @ cramertjやその他の人は、人間工学的にバインドされた

私は自分のコードをFutures0.3( Pinを使用)に移植した経験について長い投稿を書い

Unpinはほとんどすべてのタイプで自動実装されるため、ほとんどの場合、 Unpinについて心配する必要はありません。

したがって、 Unpinについて心配する必要があるのは次の場合だけです。

  1. 他のタイプよりも一般的なタイプがあります(例: struct Foo<A> )。

  2. そして、そのタイプのピン留めAPI(たとえば、 Future / Stream / Signal )を実装する必要があります。

その場合、これを使用する必要があります:

impl<A> Unpin for Foo<A> where A: Unpin {}

またはこれ:

impl<A> Unpin for Foo<A> {}

impl<A> Future for Foo<A> where A: Unpin { ... }

これは通常、 Unpinが必要な唯一の状況です。 ご覧のとおり、これは通常、タイプごとにUnpin約2回使用する必要があることを意味します。

非コンビネータの場合、またはFuture / Stream / Signal実装しない型の場合は、 Unpinを使用する必要はありません。まったく

したがって、 Unpinが発生することはめったになく、実際にはFuture / Stream / Signalコンビネータを作成する状況でのみ発生します。

だから私はMoveFromPinような名前を強く支持しています。 固定は、ほとんどの人が対処する必要のないニッチな機能であるため、名前の長さを過度に最適化する必要はありません。

まれな状況で数文字を節約するよりも、認知的利点(二重否定を回避する)の方がはるかに重要だと思います。

特に、ピン留めはすでに理解するのに十分難しいためです! ですから、不必要に難しくしないようにしましょう。

@jonhoo impl Unpinは、人々が頻繁に実装することを覚えておく必要があるものだと思いますか? 今日の図書館の作者がカスタムタイプの#[derive(Debug)]またはimpl std::error::Errorを忘れることが多いのと同様に、これらの図書館の使用が難しくなっていますか?

impl Unpinを忘れることはできないと思います。作成者が忘れると、コンパイラエラーが発生し、最初からクレートが公開されなくなるためです。 つまり、 #[derive(Debug)]はまったく異なります。

@withoutboatsが話している状況は、 Future / Stream / Signalコンビネータを実装している人だけのものであり、他の人には影響しません(特に、影響しません)ライブラリのダウンストリームユーザー)。

(二重否定はその一部だと思います。おそらく、厳密に必要なものよりも否定的なものもあるかもしれませんが、それだけではないと思います(ここで内省しようとしています)...「固定解除」は少しです、比喩的?間接的?説明すると意味があり、「固定」自体はすでに比喩であるため、なぜ私の脳がその比喩をさらに取り入れることに問題があるのか​​は明らかではありませんが、それにもかかわらず、私の脳は何らかの理由で「固定を解除」すると、あいまいになり、しっかりと固定するのが困難になります。)

あなたは正しい@cramertjだと思います。通常の意味では実際には二重否定ではありませんが、 @ alexcrichtonや@glaebhoerlのように、私はそれにつまずき続けます。 接頭語として「未」は、それに非常に否定-yの感覚(「安全でない」、「未実装」というように、私は通常、その接頭辞が発生したかである)を有しており、それが唯一の動詞としてならば、ここでのピニング否定されます。

@withoutboatsそこにあるあなたのポイント2に興味があります。 impl Unpinは、人々が頻繁に実装することを覚えておく必要があるものだと思いますか? 今日のライブラリ作成者がカスタムタイプの#[derive(Debug)]またはimpl std :: error :: Errorを忘れることが多いのと同様に、これらのライブラリの使用が難しくなりますか?

絶対違う! ユーザーが将来ではない一般的なFutureを手動で実装している場合、安全でないコードを記述せずに、つまりPin<&mut Self>を処理するために、将来の実装で状態を変更できるようにしたいと思うでしょう。 &mut self 。 MyFuture<T: AsRef<[u8]>>がUnpin実装していないことを示すエラーが発生します。 この問題の最善の解決策は、 Unpinを実装することです。 しかし、これが与える唯一の影響は、 Futureを実装しようとしているユーザーにあり、コードがコンパイルされないため、忘れることはできません。

@withoutboatsが話している状況は、Future / Stream / Signalコンビネータを実装している人だけのものであり、他の人には影響しません(特に、ライブラリのダウンストリームユーザーには影響しません)。

私は特に、ジェネリックがUnpinなくても、 Unpin包括的インプを持っているはずの非コンビネータジェネリック手動先物について話しています。

「手動のフューチャー/ストリームを実装するときにピン留めを行うにはどうすればよいですか?」という質問のフローチャートを作成しました。

pinning-flowchart

もう少し自転車に乗るために、今日の昼食時に私はLeavePinを思いついた。 それは、暗黙の誤解を招くセマンティクスなしで、 escapeと同じトーンを運びます。

生涯と同様に、 implスペシャライゼーションとピンの間に興味深い相互作用はありますか?

部分文字列「specializ」は、問題の議論を

@viは、健全性の相互作用がないだけでなく、健全性の相互作用がある可能性もありません。 pin APIは標準ライブラリで厳密に定義されており、新しい言語機能は含まれていません。ユーザーはサードパーティのライブラリでも同じように簡単に定義できます。 このライブラリコードが存在するにもかかわらず言語機能が不健全である場合、それは不健全な期間です。これは、すべてのユーザーが今日このライブラリコードを記述でき、正常にコンパイルされるためです。

このライブラリコードが存在するにもかかわらず言語機能が不健全である場合、それは不健全な期間です。これは、すべてのユーザーが今日このライブラリコードを記述でき、正常にコンパイルされるためです。

pinに関係があるはずではありません...しかし、私はそれがそれほど単純ではないと思います。

ライブラリ機能がunsafeを使用しているときに、動作がまだ指定されていない言語構造を使用している場合(たとえば、 &packed.field as *const _ 、またはABIに関するさまざまな仮定を行っている場合)、追加の言語変更を行うと、仮定が無効になります。それらのライブラリの中で、言語の変更ではなく、不健全なライブラリであると思います。 一方、言語の変更によって定義された動作が不健全になる場合、それは言語の変更のせいです。 したがって、安全でない言語の変更に直面した場合、正常にコンパイルすることは、ライブラリの健全性にとって十分条件ではありません。

MoveFromPinまたは同様のものに+1

「自分のタイプのUnpinをいつアン実装する必要がありますか?」という質問をした場合、代わりに「自分のタイプのMoveFromPinをいつアン実装する必要がありますか?」と質問すると、答えははるかに明確になります。

「ここにバインドされた特性としてUnpinを追加する必要がありますか?」と同じです。 vs「ここにバインドされたトレイトとしてMoveFromPinを追加する必要がありますか?」

固定はありません!移動

これがどこかで言及されていたら申し訳ありませんが、私はここのピンの周り、実装の問題、およびRFCの問題で行われた膨大な量の議論をざっと見ただけです。

さびはこれまでにありますか!移動しますか? 確かにそのようなユースケースは見られます(動かせないタイプで足を撃たない方法を探していたので、ピンを探しに来ました)。 答えが「はい」の場合、それはピンとどのように相互作用しますか? ピンが存在すると、!Moveを追加するよりも難しくなりますか?

上記のマージする傾向のある最後のコメント期間が完了しました。

Unpinの命名に関しては未解決の懸念があるはずです。

@RalfJungが指摘したように、#55992は、 https: //github.com/rust-lang/rust/issues/55766#issuecomment-438316891などで要求された追加のドキュメントを少しだけ追加し

drop()メソッドがPinではなく&mutselfを受け取るのはなぜですか。

そうですね、drop()は古いので(Rust 1.0以降に存在します)、変更することはできません。 Pin <&mut Self>を使用するようにしたいと思います。そうすれば、Unpinタイプは現在のように&mutを取得できますが、これは下位互換性のない変更です。

この変更を下位互換性のある方法で実装できるかどうか疑問に思いました。 AIUIはUnpinを追加するまで(そして人々は!Unpin指定できます)、すべてのタイプがUnpin実装します。 したがって、特性を追加できます。

trait DropPinned {
    fn drop(Pin<&mut> self);
}

次に、この特性をすべてのUnpinタイプに実装します。これは、オプトアウトできるようになるまではすべてのタイプです。 何かのようなもの:

impl<T> PinDrop for T where T:Unpin + Drop {
    fn drop(Pin<&mut T> self) {
        Drop::drop(self.get_mut());
    }
}

その後、我々はに、コンパイラ挿入呼び出しがあるだろうDropPinned::dropの代わりにDrop::drop 。 基本的に、トレイトDropPinnedはDropではなくlang-itemになります。 AFAICSこれは、このメカニズムがUnpinと同時に導入された場合にのみ、下位互換性があります。

Unpinの命名に関しては未解決の懸念があるはずです。

@tikue FCPの前または最中にrfcbotに懸念を表明したライブラリチームメンバーはいません。また、 Unpinに関する議論はこのスレッドにとって新しいものでも斬新なものでもないと思います。したがって、通常、プロセスでは現在のファイナライズするネーミング。 明らかに、誰かが懸念を持っている場合は、声を上げる必要がありますが、チームのチェックボックスの後にFCPを付けることのポイントは、提案どおりにAPIを安定させるために全員が快適に過ごせるようにすることです。

@cramertj私は少し混乱しています。 何人かの人々が声を上げました。 Unpinの命名に関する議論が提起され、解決された他の場所についての参照を求めたとき、私はhttps://internals.rust-lang.org/t/naming-pin-anchor-を指摘されましたUnpinの命名について不平を言う人もいて、本当の反論はありません。 https://github.com/rust-lang/rfcs/pull/2349の元のRFCにも、 Unpin代替案がなぜ悪いのかについての論理的根拠はあまりありません。 このスレッドでも、実際に出てくる反論は「短い」と「技術的に正しい」だけのようです。 理解しやすい代替名( MoveFromPin )が議論され、拒否される具体的な議論を指摘できますか?

以前のコメントで、このスレッドで斬新な視点が持ち上がったと思う理由を説明しました。 私は開始以来、ピンAPIの議論をかなり綿密に追跡してきましたが、このスレッドの前に二重否定の問題が発生したことを覚えていません。

@tikue私が提起し、二重否定の問題が何度も提起されているのを見てきました。 Unpinの正確な命名は何度も問題として提起され、 Unpin支持して一貫して解決されています。 Unpin名前が明らかに含まれていない上記の安定化提案に同意しました未解決の質問としてのUnpin :代替案について説明しました。このFCPは、 Unpinという名前を含め、行われた決定を安定させる準備ができていることを決定するためのプロセス

@cramertjその議論が行われた場所へのリンクを提供してUnpinを支持する議論を見たいと思います。 前述のように、これまでに提供した参照では、 Unpin命名に関する解決策は提供されていません。

@ cramertj + 1から@jonhooの質問。 公式チャンネルに登録されていないlibsチーム同士の議論がある場合は、それらの議論の主な目的をここで繰り返す必要があると思います。 RFCの決定は公に知られている議論に基づいてのみ行うことができるという公式の規則さえあると思いますか?

RFCの決定は公に知られている議論に基づいてのみ行うことができるという公式の規則さえあると思いますか?

うん、それは本当だ-そして記録のために、私はlibsチームにいないので、libs-teamだけの議論には参加していません。 RFCスレッドを通してみると、 Pin追跡問題は、の名前を何回ありましたUnpin育ったが。 「ダブルネガティブ」という言葉を具体的に言う人は誰もいませんが、「 !Unpinはダブルネガティブです」という言葉に加えて、できることの特性に名前を付けるという一般的なAPIルールを覚えています。むしろ、私は先に指摘したとおりに(、私は考えることができないものよりも、それらをどうUnpinそれが動詞として「固定解除」を聞くのではなく形容詞としてそれを聞い必要が実現しても、実際にこれらの規則の両方を次の「ピンではない」、これは人々にとって直感的ではありません)。

仕事、例えばしない@wmanley impl<T> Drop for Vec<T> 、我々が持っていないので、壊れるVec<T>: Unpin 。 また、このスレッドでも、ます。 返信する前に、ディスカッションをお読みください。 私はそれがかなり多くのことを求めていることを知っています、しかしそれは同じ問題が何度も何度も説明されるのを避ける唯一の方法です。

RFCの決定は公に知られている議論に基づいてのみ行うことができるという公式の規則さえあると思いますか?

これは、非公式に「新しい論理的根拠なし」ルールとして知られています。

これをどこに投稿すればよいかわかりませんが、誰かがhttps://github.com/rust-lang/rust/issues/56256を見てみませんか?

、#56256に関連するimpl<T> From<Box<T>> for Pin<Box<T>>安定化されてようOPに記載されていないが、一度それは暗黙的に安定となりますPinません。 自明ではなく、安定化のために検討する必要がある他の特性の実装はありますか? (ドキュメントをスキャンすると、他のすべてのドキュメントは、ラップされたポインターの実装を委任するのは簡単なようです)。

今日、libsチームでこの問題について話しました。 議論の最後の数週間は、特にUnpin命名に関して、最初に取り組むべきことがまだいくつかあることを示しました。

したがって、現時点ではこれを安定させることはしません(FCPにもかかわらず)。

誰かがこのスレッドでアイデアを集めて、命名状況を改善するための独立した提案を準備することができれば幸いです。

@キムンディ

誰かがこのスレッドでアイデアを集めて、命名状況を改善するための独立した提案を準備することができれば幸いです。

これは、libsチームが現在のAPIをそのまま安定させることを望まないことを意味しますか? 個人的には、現在実装されている名前のセットよりも優れた名前を思いついた人はいないと思います。そのため、そのような提案をすることはできませんが、これらのAPIが安定することを非常に気にしています。したがって、libsチームの誰かが好きな名前を持っている場合は、:shipit:

たくさんの同僚と一緒にこれをバイクシェッドし、 @ anpはDePin提案しました。これは、 Unpin 「ピンではない」という意味を取り除き、タイプについて話していることを強調しているため、実際にはかなり気に入っています。これはde- Pin 'dになります。

@Kimundiあなたまたはlibsチームの誰かが、明示的な「fcpの懸念」を登録して、これをFCPから

@rfcbotはUnpinの命名に関する

FCPを入力すると、これが実際に機能するかどうかはわかりませんが、 Unpinトレイトの名前付けに正式なブロッキングの懸念を置きたいと思います。 Unpin特性は、APIにとって非常に重要であるように見え、「ダブルネガティブ」(上記で説明)は、それを読むたびにスピンを引き起こします。

いろいろな名前についてたくさんのコメントがありましたが、残念ながら私はまだそれほど興奮していません。 私の「お気に入り」はまだMoveFromPinまたはRelocateの線に沿っています(私の神はここに非常に多くのコメントがあり、これをレビューする方法がわかりません)。

個人的には、 Pin自体の名前と、 Unpin特性を実装していないZSTのPinnedの名前は問題ありません。

Unpinの名前が​​ここでの大きな論点であるという@alexcrichtonに完全に同意します。 提案された機能自体について私が見る限り、技術的な懸念はないと思います(ただし、コメントはたくさんあるので、何かを見逃している可能性があります)。

Pinnedを含むものは実際には固定されていないため、 PinnedはZSTの奇妙な名前だと思います。 Unpinはないだけです。 PhantomPinned (#55992で名前が変更されたため)には、ZSTが_本当に_約Unpin場合、 Pin参照するという同じ問題があります。

また、この機能の微妙さを考えると、ドキュメントにはさらに作業が必要だと思いますが、それはおそらくブロッカーとしてカウントされません。

また、 @ Kimundi 、libsチームがこれを解決するためにもう少し時間を与えてくれることを嬉しく思います。 不必要なバイクシェッドのように見えるかもしれませんが、私(そして他の人もそうです)は、この機能の教えやすさを改善することが非常に重要だと思います:)

@jonhooに同意して、 Pinnedまだ奇妙に感じており、 PhantomPinnedはこれ以上良くはありません(ファントムの方法でも、ピン留めされていません)。 Unpinに適した名前が見つかった場合、 Pinnedは当然Not{NewNameForUnpin}に名前が変更されるのに役立つと思います。

私は本当に私たちが議論任意のより多くの時間を費やす必要はないと思いますPhantomPinnedこのタイプはほとんどエンドユーザーに表示されることはありません- 。 PhantomNotUnpin / PhantomNotDePin / PhantomNotMoveFromPin /などは、それを多かれ少なかれ一般的にしたり、APIをすでに十分に快適なユーザーに多かれ少なかれ明白にしたりすることはありませんAPIを使用して、 PhantomPinned正当な使用法を考え出しました。

簡単なアイデア:トレイトMoveとZST Anchor 。

Pinに固執するAnchorが含まれていない限り、すべてのタイプはMove Pinです。
Anchor: !Move直感的に意味をなす方法が好きです。

特にPhantomPinnedに時間を費やすことを提案しているわけではありませんが、 Unpin着陸したものはすべて機能する可能性があるため、オープンマインドを維持するのは簡単ではないと思います。 PhantomPinnedについても同様です。

Moveについて説明し、なぜそれが不適切なのかを何度も説明しました。 すべてのタイプは、固定されるまで移動可能です。 同様に、 Anchorは以前に提案されていますが、 Unpin ing / Move ingからオプトアウトするために使用されていることは名前からはまったく明らかではありません。

@stjepang !Unpinことが実際に移動を妨げないため、 Moveはしばらく前に破棄されたと思います。 タイプが下にある場合のみですPin 、とそうではありませんUnpinあなたはそれを移動しない「契約義務を負っ」されていることを、。

元のコメントで@withoutboatsは言った:

Pinラッパーは、参照するメモリを所定の位置に「固定」するようにポインタを変更します

値が「固定」されていないため、型がUnpin実装するのは奇妙だと思います-メモリは固定されています。 ただし、「安全に移動できる」値について話すことは理にかなっています。 Unpin名前をMoveSafeどうなりますか?

UnwindSafe特性を考えてみましょう。 これは単なる「ヒント」マーカー特性であり、安全に実装できます。 !UnwindSafe値がcatch_unwind境界を越えるようにすると( AssertUnwindSafe )、不変条件を無効にすることで値を「壊す」ことになります。

同様に、 !Unpin / !MoveSafe値がある場合でも、(もちろん)移動できますが、自己参照を無効にすることで「壊す」ことができます。 コンセプトは似ているようです。

特性Unpin実際にはMoveSafe意味します。 Pin後ろのメモリから移動できる値についてではないように私には思えます。 むしろ、それはあなたがそれらを動かしたときに「壊れない」値についてです。

MoveSafeは、 Moveと同じ問題があります。すべての値は、どのタイプでも安全に移動できます。 値が固定された後でのみ、値を移動できなくなります。

MoveSafeは、 Moveと同じ問題があります。すべての値は、どのタイプでも安全に移動できます。

そうですが、 UnwindSafeように、「安全」の意味は異なります。 とにかく、私はRelocateまたはそのようなもので大丈夫でしょう。

要約すると、トレイト名はDePin 、 Unpin 、または名前に「ピン」が含まれるものであってはならないと思います。 私にとって、それが混乱の主な原因です。 トレイトは、実際にはPinの束縛からの「エスケープハッチ」ではありません。トレイトは、移動しても値が無効にならないことを示しています。

PinとUnpinは完全に別のものだと思います。 :)

私は正反対を感じます;)。 特性は、Pinに関してのみ意味があります。これは、基になる値の移動可能性に対する制約を意味のある形で表現する唯一のタイプです。 ピンがないと、ピンを外してもまったく意味がありません。

ピンボードに何かを置くことと考えるのが好きです。 ボードに固定されているオブジェクトのピンを外すと、オブジェクトを移動できます。 ピンを外すとピンが外れます。
私はUnpinという名前が好きです。

また、!Unpinが二重否定であり、混乱を引き起こす可能性があることもわかります。 しかし、どれくらいの頻度で!Unpinを書く必要があるのだろうか。

Unpinのもう1つの名前は、 Detachです。 ピンボードのメタファーに戻ると、ピンを_アンピン_するのではなく、オブジェクトからピンを_デタッチ_します。

私はDePin本当に好きだと思います! これまでのところ、これは私のお気に入りです。簡潔で、明らかに動詞であり、形容詞ではありません。また、 !DePinもかなり明確に見えます(「ピンを外すことはできません」)。

値が「固定」されていないため、型がUnpinを実装するのは奇妙だと思います。つまり、メモリは固定されています。

値はメモリに固定されます。 しかし、ここでも価値は非常に重要です。 私にとって、メモリの固定とは、参照できない状態を維持することですが、 mem::swap違反することはありません。 値をメモリに固定することは、その固定された値を他の場所に移動しないことです。これはまさにPinことです。

また、!Unpinが二重否定であり、混乱を引き起こす可能性があることもわかります。 しかし、どれくらいの頻度で!Unpinを書く必要があるのだろうか。

あなたが名前を混乱させないと言うとき、私はあなたを聞きます。 私とこのスレッドの他の多くの人は、名前に混乱していることに気づきました。

手元にコードがありませんが、先物を初めて使用しようとしたimpl Future<Output=T>を返す関数が必要

「ああ、それがピンの意味ならそうだ。だからアンピンは..ボックスを意味するのか?なぜそれが特徴なのか?」

「待って、 impl !Unpin ?なぜそれがimpl Pinないのですか?」

「そうです、ピン留めとピン留め解除...は正反対ではありません。まったく異なるものです。それでは、ピン留め解除はどういう意味ですか?なぜそれと呼ばれるのですか?」

「いったい!Unpinどういう意味ですか?そうではありません...他の、ピンではありませんか?Hrrrgh」

それでも、これが私の頭の中で理にかなっている唯一の方法は、「unpin」を「relocatable」に置き換えることです。 「タイプはメモリ内で再配置できません」は完全に理にかなっています。 それでも、ピンが何をするのかを知っていても、「タイプは

Unpin名前をRelocatable (またはRelocate )に変更すると、私の投票が得られます🗳。 しかし、私はUnpinよりも他の提案のほとんどを見つけるでしょう。

特性は、Pinに関してのみ意味があります。これは、基になる値の移動可能性に対する制約を意味のある形で表現する唯一のタイプです。 ピンがないと、ピンを外してもまったく意味がありません。

これにポイントを置くために-ピンの周りの保証は完全に特定の動作の周りであり、タイプではありません。 たとえば、 ()を生成するジェネレーター関数は、繰り返し再開することでFnOnce簡単に実装できます。 このタイプはUnpin実装しない可能性がありますが、そのyield状態は自己参照である可能性があるため、 FnOnceインターフェイス(それ自体を移動する)は、 FnOnceときにまだ固定されていないため、完全に安全ですUnpinは、タイプの固有のプロパティではなく、タイプが固定された後(つまり、タイプを移動した後)に安全であると主張される動作の種類に関するものです。

皮肉なことに、 Unpin命名は過去に間違いなく議論されてきましたが、それについての議論は、 MoveをUnpinに置き換えることを選択したときに目撃したことを覚えています。 明白な改善です。 そして、多くの場合、後で気付くだけです。現在の設計は以前のものよりも明確に改善されていますが、さらに改善する余地がいます。 これが私がこれに向かっていた方向です。

固定解除は、具体的には、型が固定された後(つまり、移動)に安全であると主張される動作の種類に関するものであり、型の固有のプロパティに関するものではありません。

固定されたときの動作は、共有されたときの動作/不変条件と同様に、タイプの固有のプロパティです。 これについては、このブログ投稿で詳しく説明しました。

@RalfJung私たちは超えて話している。 自己参照型は「移動できない」という混乱がたくさんあります。これは正確ではなく、移動できない特定の状態に入ることができますが、他の状態にある間は完全に安全です。それらを移動するために(そして私たちのAPIは、それらを移動する機能に依存しています。たとえば、それらにコンビネータを適用したり、 Pin<Box<>>に移動したりします)。 これらのタイプが「移動できない」というわけではないことを明確にしようとしています。

ああ、はい、それなら私は同意します。 !DePinタイプは常に固定されているわけではなく、

@cramertj @withoutboats私がまだ十分に推測できていないことの1つは、 Unpin名前を変更することに反対ですか? 名前を変更する必要があることに同意しているとは思えませんが、反対かどうかはわかりません。

個人的には、ここでの主な問題はUnpinという名前にあるとは思いません。「名前を変更できれば、すべてが直感的になります」。 名前の変更は少し役立つかもしれませんが(そしてRelocate / DePinはここではいいようです...)、主な複雑さはそれ自体を固定するという概念から来ていると思います。 それは確かに理解または説明するのが最も簡単な概念の1つではありません。 ほとんどありません。

したがって、 core::pinモジュールのドキュメントを_大幅に_強化する必要があり、さらに多くの例を含める必要があると

@alexcrichton私は名前の変更に反対していません。 Unpinはもう大丈夫だと思いますが、 DePinで大丈夫だと思いますので、提案しました。 RemoveFromPinは最も明らかに「正しい」ですが、構文上の塩の点で冗長なので、私はその名前に特に反対すると思います。 ただし、誰もが同意する名前が最適であることがわかるまで、安定化を無期限に延期することに反対しています。APIには、 Unpin名前のために劇的に良くも悪くもならない、固有の複雑さがいくつかあると思います。 futures_api周りのコード、ドキュメント、およびディスカッションへのチャーンが増えるのを防ぐために、すぐに安定化に向けてプッシュしたいと思います(したがって、 futures_apiを安定させるためのピースの準備を開始できます自体)。 おそらく、意見を持っているすべての人が解決策を提案し、これを解決するためのより高い帯域幅の機会を得ることができるように、専用の名前解決VC会議をスケジュールする必要がありますか?

私の投票はstd::pin::Reloc (移転)です

私には2つの非常に架空の状況があります(実際には1つだけですが、2つの異なる実行)。許可されているかどうかを明示的に述べてもかまいません。

Unpinは、固定された型状態(私はその用語を正しく覚えていることを願っています)の間にすべてのT ( T: Unpin )状態から移行するのは簡単であることを示します@RalfJungのブログ投稿の1つから)ピン留めされていないtype-stateへ。 これが、 Pinが&mut T Pinことができる理由です。

それで、ピン留めされたタイプ状態からピン留めされていないタイプにある間、すべてのWoofの状態から常に遷移することも可能なタイプWoofを作成したいとします-状態ですが、そうすることは簡単ではなく、したがってUnpin実装できません。 Woofがfn unpin(self: Pin<Box<Woof>>) -> Box<Woof>ような関数を持つことは許可されますか?

ピン留めされたものからピン留めされていないものへの移行がたまにしかできないタイプMeowと、 fn unpin(self: Pin<Box<Meow>>) -> Result<Box<Meow>, Pin<Box<Meow>>>ような関数についても同じです。

バイクシェディングのための私の2カラット:

私が正しく理解していれば、 Unpin実際に意味するのは、「 Pinは私に影響を与えない」ということです。

BypassPinやIgnorePinようなものはどうですか?

したがって、ピン留めされたタイプ状態からピン留めされていないタイプ状態にある間、Woofのすべての状態から常に遷移することも可能なタイプWoofを作成したいとしますが、そうすることは簡単ではありません。したがって、Unpinを実装できない場合、Woofがfn unpin(self:Pin:Pin)のような関数を持つことは許可されますか?>)->ボックス?

はい、それは可能であるはずです。 たとえば、 Woofが侵入型リンクリストの要素である場合、リストから要素を削除し、同時にPinを削除する関数を提供できます( -エンキューされたWoof s、2つのタイプステートは同等であるため、ピンを解除できます)。

Relocateは、私にとってRePinと同じ問題があります。これは、値を完全に固定解除するのではなく、固定された場所から別の場所に値を移動できる操作を意味する可能性があります。 これはRePinほど強くはありませんが、それでも少し混乱しているようです。

値を完全に固定解除するのではなく、固定された場所から別の場所に値を移動できる操作を意味する場合があります。

これは正確にはこの特性の目的ではありませんが、完全に間違っているわけでもありません。ほとんどのユースケースでは、固定された場所から別の場所に値を移動することは、自由に使用するのと同じくらい壊滅的です。 実際、誰でもPin<Box<T>>を作成できることを考えると、なぜここで根本的な区別をしているのかさえわかりません。

ほとんどのユースケースでは、固定された場所から別の場所に値を移動することは、自由に使用するのと同じくらい壊滅的です。

はい、そのため、その操作を型に追加するトレイトがあると便利です(内部参照の更新などを行う必要がある場合があるため、これはUnpinようなマーカートレイトではありません)。 私は今このようなものを追加することを提案していません、ただそれが将来( stdまたはサードパーティのいずれかで)提供されるのを見ることができ、名前の紛らわしい重複に終わる可能性があるものです。

今のところ、その強力なユースケースはわかりませんが、ピン留めされたコレクションでそのようなものを使用することを考えました。

さて、ここにいくつかの考えがあります。 最初は、 PinInertやPinNoGuaranteesような名前を付けることができるかどうか疑問に思っていましたが、それは説明でもありますが、私が本当に必要としているのは、アクションではなく

Unpin問題の1つ(理由はわかりません)は、意図した意味が「ピンから外す、ピンを外す、安全に実行できる」ということを頭から理解できないことです。 "。 そのままの特性は「ピンを外す行為」という行動を伝えますが、読んでみるとよくわからないようです。 Pin<T: Unpin>があるのは奇妙に感じます。なぜなら、それが「ピン留め解除」の場合、なぜピンになっているのでしょうか。

CanUnpinような名前でうまくいくのだろうか? これの多くは、何らかの方法でハード保証に関するものではありません( Unpin実装することは、ピンから削除することを意味するのではなく、ピンから削除できることを意味します)。 それはどのように聞こえますか? CanUnpinは他の人にも十分に読めますか? 十分に短いですか?

(接頭辞Can付けると、動詞がはるかに簡単に伝わります。ピンから削除できると表示されますが、必ずしもそうなるとは限りません)。


別の無関係な接線として、私が以前に提起するのを忘れた1つのこと(それについては申し訳ありません!)は、上記のすべての方法が必ずしも固有の方法である必要があるとは思わないということです。 推論と衝突メソッドに関するバグはすでにたくさんあり、 Derefも実装する型にas_refやas_mutなどの非常に一般的な名前を追加することは問題のようです。起こるのを待っています。

これらの操作は、危険な場所に配置することを正当化するのに十分な頻度で発生しますか? (本質的に)または、関連する機能の安全なルートを胃に入れることができるほどまれにしか使用されていませんか?

私は今このゲームにスキンを持っているようです: CanUnpinはUnpinnableまたはそれに類似したものに精神的に非常に近いようです、そして私の印象は常にコミュニティがそのような種類の修飾子に眉をひそめているということですほとんどの特性はタイプが実行できるアクションを説明するため、特性名。 impl自体は、その意味で暗黙の「can」または「-able」です。 何が決定されても(そして、ここに記載されている範囲では名前は重要ではありません-IM-not-so-HO-これを入力する必要のあるユーザーはほとんどいないでしょう)、ここで質問を解決することについて緊急を感じることをお勧めします。 私はこの安定化に興奮しています、そして私は多くの人々がそうであることを知っています!

@alexcrichton

Unpin問題の1つ(理由はわかりません)は、意図した意味が「ピンから外す、ピンを外す、安全に実行できる」ということを頭から理解できないことです。 "。

T: Unpinを「 TはPin影響を受けない」と考えるとどうなりますか? 次に、 Pin<T: Unpin>がある場合は、 Pin内に値があり、ピン留めの影響を受けないため、ピン留めは事実上意味がありません。

言い換えれば、 Unpin Pin中和します。 正直なところ、多くの議論の末、私はこれを一種の内面化したので、今では理にかなっています。 😆

これの多くは、何らかの方法でハード保証に関するものではありません( Unpin実装することは、ピンから削除することを意味するのではなく、ピンから削除できることを意味します)。

これに対する対位法は、 Send特性です。 値を送信するという意味ではなく、送信できるという意味です。

@anp CanUnpinは素晴らしい名前ではないことに同意します。私は、より良い名前を見つけるために最善を尽くそうとしています。 Unpinはそもそも名前を変更する必要があると私が感じるのと基本的に同じ理由ですべての提案が打ち切られるように見えるので、これを名前変更する必要があるとまだ感じている人はほとんどいないようです。

また、他のすべての変更と同様に、安定化が列車に乗ることを思い出してください。来週のリリースでは、これは間違いなくそのリリースにはならず、次のリリースの候補になります。 つまり、できるだけ早く着陸するために、これらすべてを着陸させるのに7週間、ほぼ2か月あるということです。 緊急性が必要であることに同意しますが、それは「月曜日までにこれを解決しよう」という緊急性ではなく、単に「この緊急性を忘れないでください」です。

@stjepang私も、よく考えてUnpin慣れてきました! この時点でのすべての代替名は非常につまらないように思われるので、より良いドキュメントを作成することにしました。 理想的には、 Pin apiについてあまり知らない人を見つけて、後で上記のドキュメントを読んで、学習に十分であることを再確認したいと思います。

また、この問題にとって特に重要であると感じているため、改善されたドキュメントの安定化を正式にブロックしたいと思います。

@rfcbotはドキュメントの改善に関係しています

具体的には、より良いドキュメントが必要だと私は感じています。

  • なぜそれが安全であるか、および/またはなぜそれが安全でないかについて、各方法についてより多くの散文。 たとえば、 PぎDerefMut実装の契約は、「合理的に動作する」とPin::new_unchecked文書化されていません。
  • 各unsafe関数には、安全でない理由のコード例、または少なくとも、失敗する可能性のある一連の手順の明確な説明が必要です。
  • モジュールのドキュメントは、単にPinとは何か、そしてそれが何を意味するのかを超えて、より詳細に説明できると思います。 「これは一般的な不動のタイプではなく、その形式である」や「 Pinが実際に、たとえば先物でどのように使用されるか」などの情報が役立つと思います。
  • ドキュメントまたはコード例のいずれかに、ジェネリックスとUnpin例を見たいのですが。 たとえば、将来的には多くのコンビネータがこれを処理する必要があるように思われます。同様に、一部のコンビネータは特にこれをUnpinかなり理解するのに役立つかもしれません。

他の人がドキュメントに追加する具体的な実用的なアイテムを持っているかどうかも知りたいです!

次に、 self質問をas_refで正式にブロックしたいのですが、これはすぐに解決できると思います。 (これを早く持ち出すことを忘れて申し訳ありません)

@rfcbotは自己メソッドに関係します

これは、 as_ref 、 as_mut 、 get_ref 、 get_mut 、 into_ref 、およびsetをすべてのメソッドに提案します。 Pinタイプ。 この提案では、メソッドの衝突のためにスマートポインターに対してこれを行わないと述べていますが、今日実装されているPin APIに一貫性がなく、邪魔になることがよくあることを経験が示していると述べています。

ただし、これらのメソッド名は非常に短くて甘い名前であり、エコシステム全体でよく見られます。 libsチームは、過去に終わりのない一連のバグに遭遇しました。そこでは、トレイトの実装などが追加され、さまざまなクレートの既存のトレイトメソッドとの衝突が発生します。 これらの方法は、その名前から特にリスクが高いようです。 さらに、名前が最終的に機能するようになったとしても、将来追加される名前は衝突のリスクが高いため、将来的にPinにメソッドを追加できるかどうかは不明です。

この慣習からの逸脱を再考したいと思います。個人的には特にその結果を心配しており、これらの危険を回避するための中間点を見つけることができれば素晴らしいと思います。

そして、私がそれに取り組んでいる間、私が気づいた最後のことは、これもかなり迅速に解決できると思います

@rfcbot懸念box-pinnned-vs-box-pin

名前たBox::pinのために検討されてBox::pinned ? Pinnedおよび/またはPhantomPinnedするので、名前が戻り値の型と一致するのであれば、それは素晴らしいことのようです。

@alexcrichtonオプションとしてMoveFromPinに対して特に説得されたものはありますか? 以前は好意的だったと思います(そして複数の人もそれを気に入っているようでした)。 記録のために、私が覚えている、またはCtrl + F-ingですぐに見つけることができる直接の反対意見は、 「フレーズ全体である名前は非常に単調である」 (@withoutboats)ことと「言葉が多すぎる」 ( @cramertj)。

「よく考えてから…慣れてきた」というモチベーションがかなり不安になっていることは認めざるを得ません。 人間は本質的に何にでも慣れ、事後的に合理化することができます。 我々は最終的にそれに慣れるまでに終了しなかった場合、名前が何であるかに関係なく、それは大きな驚きだろう。 (これが、他の場合に一時的なものとしてexistential typeような明らかに次善の名前を選択して、永続的にならないようにする理由です。)

経験豊富なユーザー(私たち全員です!)の視点を、より新鮮な頭脳を持って物事に取り組む他のユーザーよりも誤って特権を与えることは、私たちが作ることに対して常に警戒する必要があると私が思う一種の間違いです。

それは確かにstdlibのスタイルではありませんが、 CanUnpinは、その特定の部分が他の部分とどのように適合するかをどういうわけかより明確にしているようにも感じます。

ドキュメントを読まずに、初心者にUnpinの意味を説明する名前を発明することは不可能です。 SendとSyncも初心者には理解しにくいですが、 Unpinと同様に、見栄えがよく簡潔です。 これらの名前の意味がわからない場合は、ドキュメントを読む必要が

@valff確かにそれは正しいですが、ドキュメントを読んでピン留めの仕組みを理解している人はかなりいますが、 Unpin名前は依然として重大な精神的困難を引き起こしますが、他の名前はそれほど引き起こしません。

@glaebhoerl申し訳ありませんが、私は現在のUnpinよりもMoveFromPinまたはCanUnpinファンです。 結論に達するのを手伝おうとしているだけです!

私は現在の提案を理解しようとしました、多分この種の進捗報告は命名にもいくつかの追加の光を当てることができます。

  • DropがPinの作成者に追加の要件を課すまで、 Pinの保証を安定したアドレスに拡張します。 unsafe fn new_unchecked(pointer: P) -> Pin<P>を呼び出すために必要な前提条件が直接言及されていれば便利だと思います。 Pinを作成する方法を理解することは、彼らの概念であるimoを理解するのに大いに役立ちます。
  • we agreed that we are ready to stabilize them with no major API changes.で始まる追跡の問題で言及されている要約には、多くの古いAPIが含まれています。 同時に、関連するDropとピンプロジェクションに関する多くの洞察も含まれています。 また、このレポートにはない箇条書き1で言及されている追加保証の明示的な定式化も含まれています。 この古くなった情報と新鮮な情報の組み合わせは少し混乱しました。すべての情報をこの問題に移動するか、古い段落を示すことを検討してください。
  • 追加の保証はslight extensionと呼ばれているので、それをオプトアウトする何らかの方法があると思いました。 固定すると、ポインタがドロップされるまでそのポインタの型状態が一度保持されるため、この型状態から「通常の」状態に戻る方法があります。 実際、それは私が最初にUnpinをやろうと思ったことUnpinが、 Unpinは、ピン状態を必要とする内部不変条件を削除する型の操作と混同される可能性があります( Dropソフトバージョンなど)。 。

明確化:私は個人的に他の名前よりもMoveFromPinを好みますが、それは間違いなく人工的で不格好です。

@alexcrichtonによってすでに要求されているドキュメントの改善に加えて、頭に浮かぶ主要な実用的な具体的な実用的な項目は、 map_uncheckedとmap_unchecked_mutピン投影のフィールドルールの背後にある理由を説明することです。 次のようなもの:

ピンは、参照されたデータがドロップされるまで移動しないことを約束します。 構造体のフィールドは、含まれている構造体の後に削除されるため、ピンの状態は、構造体自体のDrop実装を超えて拡張されます。 フィールドに投影すると、構造体デストラクタ内でフィールドから移動しないことが約束されます。

自動派生マーカー特性の名前ElidePinはどうですか?

型は常に固定されていないかのように扱われるか、固定されていないかのように動作することがわかります。 また、 !ElidePinも非常に明確なようですが、主観的かもしれません。

標準マーカーには、ピン留めを理解するための完全な名前もありません。 Pinnedは、含まれている構造体自体が固定されているという概念を呼び起こしているようですが、固定はポインターに適用され、明示的なアクションの後でのみ適用されます。 これはそれほど重要ではありませんが、重要ではありません。特に、自己参照型の実装で最初に遭遇する型である可能性があるためです。

MoveFromUnpin / RemoveFromUnpinに対する私の一般的な反対は、それらがあまりにも言葉が多すぎて、手動のFuture実装の人間工学を後退させるということです。 CanUnpin / DePinどちらも私には問題ないようです- UnpinがRead / Writeのパターンに従っていることがより明確になったらいいのにと思います/等。 しかし、それは人々にとって直感的ではないように思われるので、構文上の塩のように見えずにこれをより明確にするものを+1します。

NotWhateverUnpinBecomesがおそらくPinnedの最良の名前であることに同意します。 とは言うものの、 Adhereは、注意することと固執することの両方を意味します。 :slightly_smiling_face:

CanUnpin / DePinはどちらも私には問題ないようです-UnpinがRead / Write / etcのパターンに従っていることがより明確になったらいいのにと思います

Readとは異なり、 Unpin難しくしていることの1つは、それがマーカー特性であるということだと思います。 Readは、メソッドRead::readがあるため、簡単に理解できます。知っておく必要のあることはすべて、 traitます。 場合はxですRead私は、私が呼び出すことができることを理解x.read() -同様にwriteのためにWriteことを説明するのは難しい、などそれのをXがUnpin X実装すると、 Pin<Ptr<X>>がDerefMut実装することになります。つまり、 Xように扱うことができます。

Read :: readメソッドがあるため、読み取りは簡単に理解できます。

auto trait定義+ GATに常に適用可能なメソッドを追加できれば、 Unpin::unpin - fn unpin<P: DerefFamily>(self: Pin<P::Deref<Self>>) -> P::Deref<Self> )を持つことができます。 .....考え直してみると、それで誰もが混乱することはないと思います;)

(もっと深刻なことに、 Pin<P<T>>からP<T>移行するためにPin::unpinをサポートします)

ピンから移動するためのPin :: unpinをサポートします

>からP

これは、現在の名前自体と同じように、私の頭の中の2つの用語を混乱させます。 unpinは、メソッドfn unborrow(&'_ T)またはfn unmove(??)があるかのように比較するために、型状態の保証を逆にするように聞こえます。 pinは、型を表すメモリがDrop::drop edになるまで保証を提供するため、実際には状態を逆にすることはありません。型は、他のすべての表現が同等の保証を維持することを保証するだけであり、したがってこれを無視できます。 。 これは、マーカーの特性とio::Readようなものとの主な違いでもあります。 1つはコンパイラーまたは言語の操作を有効にし、もう1つはプログラマーの操作を有効にします。

さらに、これは現在、正しいタイピングだけでは正確に表現できないという大きなポイントです。 演算unpinを呼び出すと、逆関数pinがあったように聞こえます。 これは関数であるため、このような固定解除操作が何らかの形で計算作業にバインドされていることを少し誤って示唆しています。たとえば、より確立された命名規則に従うことで、この点でinto_pointer()方が有利です。

最後に、計算作業を伴うunpin関数を持つタイプの可能性があると思います。 非常に特殊な内部不変条件を持つタイプは、インターフェイスfn unpin(self: Pin<&'a mut T>) -> &'a mutを提供できるように内部状態を「修正」できる可能性があります。この場合、 Pinすべての保証が一定期間失われます。 'a 。 その場合、上記の両方のポイントは適用されなくなります。 このような関数は、同じメモリ位置にドロップして再構築するのと同等の効果があるかのように想像できます(したがって、実際には型の状態を削除します)。 また、自己参照を動的割り当てに移動するなど、計算が必要になる場合があります。

紛らわしい名前によって、ライブラリの設計者や実装者がこれら2つのアイデアを混同して、紛らわしくない名前を選択するのが難しくなるとしたら、それは残念なことです。

MoveFromUnpin / RemoveFromUnpinに対する私の一般的な反対は、それらがあまりにも言葉が多すぎて、手動の将来の実装の人間工学を後退させるということです。

特にMoveFromPinの場合は、それが当てはまるとは思いません。これは、私にとっては合理的と思われます。どのような種類のスマートまたはダムのオートコンプリートでも、とにかく問題はほとんどありません。

人間工学の重要な部分の1つは、コードの可読性と理解性でもある必要があります。 Rustは、物事を積極的に省略したこと( fn 、 mutなど)に対して過去に少しの批判をすでに受けており、一部のコードが読みにくくなっています。 理解するのがさらに複雑で、ほとんどのユーザーにとってニッチな目的を達成する概念の場合、より冗長で説明的な名前を使用することは完全に受け入れられるはずです。

@rfcbotは、名前の固定解除を解決します

さて、私はしばらく煮込んでいて、これについて直接話しました。 少なくともUnpinという名前をマーカーの特性として使用することで、個人的には快適になりました。 その結果、私はブロッキングの異議を削除します(ただし、libsチームの他のメンバーの気持ちが違う場合は、お知らせください!)

私がUnpinたどり着いた主な理由は、基本的にすでに上でUnpinが(または少なくとも私の意見では)生きている慣用的な基準を満たしていません。

「この特性の名前を見たばかりで、それが何をするのか知りたい」というより良い名前があると私はまだ信じていますが、それがここで解決する正しい問題ではないと思います。 現時点では、少なくとも現時点では、この特性の別の名前を理解することはできませんが、これも慣用的なものですが、別の問題を解決する特性名に切り替えています。 Unpin理解することは、 SendやSync Unpinと同じです。何が起こっているのかを完全に理解するには、ドキュメントを読む必要があります。


もう1つの明確なポイントとして、いくつかの「ブロックする異議」をリストしましたが、それらは、ブロックする異議自体よりもTODOアイテムです。 こんなに長いスレッドをナビゲートする素晴らしい方法がないだけです! そういう意味では、FCPが1週間ほど経過した状態で、いつでも安定化PRを投稿しても問題ないと思います。 セルフ/ピン/ピン留めに関する最後のポイントは、そこで簡単に説明できます(必要な場合でも、上記の提案として残すことができます)。

この場合、特に安定化の前提条件ではないと思います。 これらのタイプが安定する前にドキュメントを追加するための完全なサイクル(6週間)があり、完全な非同期/待機ストーリーが安定する前に、ここでドキュメントを強化するためにさらに長い時間があります。 それは今そこにあるものを改善するための時間の負荷であり、そして今そこにあるものはすでに非常に役に立ちます!

ここで「慣用句」とはどういう意味ですか? Reloc[ate] 、 Escape 、 Evade 、 Pluck 、またはRoamのユニディオマティックとは何ですか? それらはすべて動詞であり、二重否定の問題があると誤解されることはありません。

@alexcrichton UnpinがDepinやDePinよりも慣用的であると見なされる理由はありますか?

Depinは非常に堅実な代替手段であり、 Unpinと同じ精神的な問題を引き起こすことはないと思います。

単純な理由の1つは、depinが単語ではなく、unpinが単語であるということかもしれません。 個人的には、 Unpin理解するのに問題はありません。 他のマーカー特性の命名と一致すると思います。

@jimmycuadra Rustには、stdlibを含め、「本物の」単語ではない名前がたくさんあります。

それが、ある名前を別の名前よりも選ぶ重要な理由と考えられたとしたら、私は驚きます。

@Pauanそれは重要な理由です。 英語を母国語とする私にとって、 Depinは、固定を解除するという言葉が存在することを忘れて、それを作り上げようとしたように聞こえます。 文法的には明らかに間違っているように思われます。「ピンを外す」という英語の単語は「ピンを外す」です。

良い例えは、「ロック解除」ではなく「ロック解除」というAPIがある場合です。

@jaredr by "idiomatic"ReadおよびWrite特性のように、標準ライブラリの確立された規則に従うことを意味します。 言葉ではない名前、動詞、可能な場合は短く、状況に適した慣習があります。

あなたが提案したような名前はすべて可能ですが、 Unpin (または少なくとも私はそう感じます)がこの特定のアクションに最も適切です(「このタイプの固定を解除できます」保証)。 他人の緩い同義語ながらUnpin 、私は主にだけ「ピン解除」離れた単語から名前を変更していると思うと時々と同じ接続伝えていないPinのようにUnpinします。

@Pauan @withoutboatsに同意します。 DepinはUnpinと同様にジャイブのようには聞こえません。

@aturonはhttps://github.com/rust-lang/rfcs/pull/2592#issuecomment-438873636に次のように記載されています。

Pinの使用は、 &selfと&mut selfません。 長期的には、 Pin自体が言語サポートを備えた別個の参照モードになる可能性があります。 これらすべての場合において、署名で伝えられているのは、呼び出し先に付与されているアクセス許可であり、 Pinは参照から移動することを禁じています。

表面上、これはネイティブの&pinタイプか何かを指します...しかし、これがPin<&mut T>などとどのように一致するかを確認してください。 @withoutboatsとの私の会話に特に@cramertjに、彼らはすべての言語サポートとどのように我々はそこから得ることができると参照のセパレートモードの考え方についてはよく分からなかったPin<T> 。

pin安定させる前に、これらのビューを調整して、同じページにいることを確認するのが賢明です。 この重要なインフラストラクチャ。 ですから、私は主にアーロンがこれを拡張し、ボートとテイラーがそれから重量を量ることを望んでいます。

他の人は、Unpinの同義語ではありませんが、主に「unpin」という単語から名前を変更しているだけであり、Unpinと同じ接続をPinと伝えない場合があると思います。

@alexcrichtonこれは実際には良いことだと思いますか? 同様にSend移動する/コピー(=)の操作、 Sync借りて(&)操作。 たぶん、そのような接続は実際にもっと混乱を引き起こしていますか?

@ crlf0710かもしれません! 私自身ですが、そのようなつながりに同意するかどうかはわかりません。 SendとSyncは、それらが有効にしていること(他のスレッドにタイプを「送信」し、スレッド間でアクセスを「同期」する)について詳しく説明しています。名前を付けるときは、ある操作または別の操作の近くに名前を付けることは避けてください

@alexcrichton正確に! したがって、この特性は、それが何を可能にしているのかについてもあるはずです。 ( "引っ越し (ここでは動詞) Pin )。 私は英語を母国語とはしていませんが、 pinから「固定を解除」するのは少し...変だと思いますか?

@ crlf0710しかし、トレイトが有効にするのは、ピンからの<moving>なく、その<moving out of a Pin>です。 moveとmoveの同義語の問題は、トレイトがタイプの移動能力を制御することを意味しますが、それは実行しません。 ピンへの接続は、特性が実際に何をするかを理解するために不可欠です。

したがって、この特性の最も慣用的な名前は、「ピンから移動する」という意味の動詞であり、「ピンを外す」という言葉は私にとって非常に明白です。 ウィクショナリーの「固定解除」の定義は次のとおりです。

  1. ピンを外して外します。
  2. (推移的、コンピューティング、グラフィカルユーザーインターフェイス)以前に固定された場所から(アイコン、アプリケーションなど)を切り離す。

@withoutboats説明してくれてありがとう! 実際、私はこのUnpin名前、または錆びたチームが最終的に決定した名前を受け入れることができると思います。ピンタイプの安定化をまったくブロックしたくないので、「移動」などの懸念を完全に理解しています。

どこかにごくわずかな「繰り返し」があるように感じます。 そして私は自分自身を説得しなければなりません:それは「ピン留めできない」という意味ではなく、その逆です。 それが最終決定であるなら、しばらくして私はそれに慣れるでしょう。

同時に、最後の提案をさせてください。実際、上記のDetach動詞も、少し一般的すぎますが、非常に優れていると思います。 私が言ったように、私はネイティブスピーカーではないので、他の人のために話すことはできません。 ちょっとしたアイデアとして見てください。

私は安全な互いに素なピン留めされた投影について考えていて、それを可能にするかもしれないアイデアを思いつきました。 これは、固定された可変左辺値に対するマッチングをシミュレートする

pin_proj! { let MyStruct { field1, field2 } = a_pinned_mutable_reference; }

Pin<&mut MyStruct>をフィールドへの固定された可変参照に分解します。

これを安全で使用可能にするには、他に2つのものが必要です。

  • 「常に- Unpin 」フィールドをマークするためのKiteタイプ
  • Unpin安全でないものにする

後者は、投影の安全性のために必要です。 これがなければ、「ピン留めされた投影でKite 」を安全に定義できますが、これは実際には間違っています。

これを念頭に置いて、安定化する前にUnpin安全でなくして、他の便利な操作を安全にする余地を残すことを提案します。

@qnighyタイプがDrop実装の固定保証に違反する可能性があるため、 DropはUnpinと同等に強力になります。 Unpin安全でなくしても、追加のコードは安全になりません。 Dropも安全であり、変更できないためです。 これについては追跡の問題について多くのことを話しましたが、このスレッドでも言及されています。

基本的に、ピンプロジェクションは、今日のRustでは表現できない特定の負の境界を表明する機能がなければ安全にすることはできません。

@ crlf0710

同時に、最後の提案をさせてください。実際、上記のDetach動詞も、少し一般的すぎますが、非常に優れていると思います。 私が言ったように、私はネイティブスピーカーではないので、他の人のために話すことはできません。 ちょっとしたアイデアとして見てください。

デタッチは、移動や再配置などの名前よりもはるかに優れているように見えますが、デタッチメタファーの他の可能な使用法と競合しているようです(エスケープが「エスケープ分析」などと競合する可能性があるのと同様)。 データがコンピュータサイエンスの他のデータとどのように関連するかについては、メタファーが非常に多いため、Unpinのもう1つの新しい利点を考えさせられます。「ピン」メタファーにしっかりと切り込むことで、将来のメタファー言語のためのスペースを占有しません。 Detach、Escape、Relocateなどの名前のように、別の目的で使用する必要がある場合があります。

@withoutboatsこのバイクシェッドへの名前や投資については特に好みはありませんが、興味があります。 Detachは他にどのような目的に適合しますか(推測した場合...)?

@Centril明確なユースケースは考えていませんが、たとえば、破壊に関連するいくつかのユースケースを想像することができます。

@withoutboatsそうですね。 乾杯!

ウィクショナリーエントリはの命名正当化するための最良の動機である場合@withoutboatsは私はわからないUnpin有効にするにはmove from a pin 。 物理的表現の比喩は、Rustで移動しても、世界のオブジェクトが奪われないという事実に苦しんでいます。 より正確には、固定されたポインタを「upinning」しても、参照されるメモリを制御できません。 Tオブジェクト表現が引き続き必要であり、ポインタのセマンティクスによって保証されるため、その割り当てと有効性があります。 したがって、ピン留めを解除しても、ボックス化解除のように、完全に制御されたT表現は得られません。 そして、 movingに関してunpinningを定義する辞書エントリは、実際には、そのような名前の正当化以上の混乱の一部です。

提案されたAPIでは、どのメソッドもそのような効果はありません(そして、ピンの範囲にも含まれていません)。 たとえば、 Pin<Box<T>>をBox<T> (さらにはT )移行する安全な方法がわかりません。また、 Unpin必要かどうかもわかりません。そのような強い可能性を持っています。 すべての違いがどこにあるのか正確にはわかりません。 しかし、私が理解している限り、 Pinは一部のメモリ位置に適用されますが、 Unpinは、 Tの表現ではピン保証に依存しないことを保証します。 しかし、それは記憶を完全に取り除いて忘れることができるのと同じでしょうか? 私はそうは思わない。 もっと具体的な例を考えてみましょう。

編集:より具体的には、 TがUnpinであっても、メモリの割り当てが解除される前に、固定されたメモリで呼び出されるT::drop(&mut)インスタンスに依存できますか? 形式主義から私が知る限り、答えはイエスでなければなりませんが、 Unpinという名前

編集2: Rc使用すると、 Pin<&T>を監視できますが、 T: Unpin 、元のメモリ位置でdrop呼び出されません。 ピンの外側で参照を存続させてください。ピンを落とした後、 Rc::try_unwrap移動できます。 https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dec6f6c6d2c0903d87a4a9cefe50a0caこれは既存のメカニズムを通じて効果的に質問に答えますが、意図したとおりに機能しますか?

おそらくIgnorePin ? TがIgnorePin場合、 Pin<Box<T>>を&mut Tとして扱うことができ、基本的にPinの存在を無視します(AIUIもBox無視します) IgnorePinはそうではないと思いますが、それが何であるかはわかりません。 IgnorePinはそれが許可するものを説明しており、型に課せられた制約を説明していませんが、 Unpinも説明していません。

@wmanley上記のコメントで、非常によく似たアイデアElidePin 、当時は、なぜそれがより正確であると感じたのかを具体的に表現できませんでした。 しかし、私は「1つの動詞」がRustマーカー特性のスタイルガイドであることに同意します。 !ElidePin / !IgnorePinの形式でより自然な否定も可能ですが、最適ではありません。

@withoutboatsフォローアップの質問:ピンは基礎となるメモリの観点から指定されているようですが、ピンはZSTとどのように相互作用しますか? PinnedはZSTであるため、ZSTでさえUnpinかどうかは不明です。 そのメモリ表現が正式に無効になることは決してないので、 T::drop(&mut self)呼び出す必要はありません。これは、たとえばリークされたBoxから&mut 'static _メモリからピンを構築する方法と非常によく似ています。 Box 。 同時に、それはすべて間違っている可能性があり、別の解釈がどのように可能になるかがわかります。 これらの設定は、ドキュメントで注目に値するように感じます。

作成し、それが安全であるPin<P<T>>とT: !Unpin 、その後すぐに値固定を解除、それは何もピニングが観察されないことを保証していますか? ポーリングのようなメソッドが呼び出された後に固定された値が移動された場合、それは未定義の動作のみですか?

@HeroicKatora残念ながら、今日では、ジェネリックスが原因で!Unpinなる可能性のある型に対して、 Dropを実装することが可能です。

struct S<T>(T); // `!Unpin` if `T: !Unpin`
impl<T> Drop for S<T> { ... }

@cramertjありがとう、ちょうどそれも実現しました。

@cramertjでは、ジェネリック境界ではデフォルトでT: ?Unpinになりますか? Sizedような既存のタイプでデフォルトでT: Unpinれない理由は他にありますか? それはそれが厄介に厳格になるいくつかの例を与えるでしょうが、その追加の自動バインドは回帰を引き起こす可能性がありますか?

@HeroicKatoraこれには、標準ライブラリのすべての型と、 Unpinをまったく気にしない一連のコードに?Unpin境界を設定する必要があります。

Drop +!Unpinタイプを作成する別の方法として、PhantomPinnedフィールドを追加することも安全です。

デフォルトでTに設定されない理由は他にありますか:既存のタイプの固定を解除しますか?

Unpinは、Send and Syncのような単なる自動特性であり、この提案には新しい言語機能は含まれていません。 したがって、自動トレイトがすでに定義されているのと同じセマンティクスがあります。つまり、デフォルトではジェネリックに適用されません(自動トレイトではなく、コンパイラに組み込まれているトレイトであるSizedとは異なります)。

ポーリングのようなメソッドが呼び出された後に固定された値が移動された場合、それは未定義の動作のみですか?

ここで、このコンテキストでの未定義の動作は、実際には従来の種類のUBではないことを明確にする必要があります。 むしろ、そのコードは、Pinの型を監視して、その値の所有権セマンティクスについて仮定を立てることができます(つまり、Unpinを実装しない場合、デストラクタが実行されるまで、再度移動または無効化されることはありません)。 コンパイラーが決して起こらないと想定しているという意味でUBではありません(コンパイラーはピンが何であるかを知りません)。

そうです、提供した保証に依存しているコードがないことを確認できるという意味でそうです。 しかし、型が固定されたことをコードが観察できないため、あなたが行ったことは確かに無意味です。

@cramertjなるほど、それは少し残念なことです。 私は、仕様ではPinとDropとの相互作用に少し不快ですが、言語ではそうではありません。 インターフェイスの正確性、使いやすさ、そして近い将来のリリースの間でかなり矛盾しています。 上記の編集2について説明を得ることができますか?

Rcを使用すると、Pin <&T>を監視できますが、Tの場合:Unpinは、元のメモリ位置でドロップが呼び出されません。 ピンの外側で参照を存続させてください。ピンがドロップされた後、Rc :: try_unwrapを使用して移動できます。 https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dec6f6c6d2c0903d87a4a9cefe50a0ca

@HeroicKatoraコードの問題は、 Mem<'a>: Unpinですが、使用する安全でないコードの行は、 Unpin実装しない型にのみ適用される仮定に依存しています。 Mem<'a>: Unpin : https :

Pin::newを呼び出すときは、Unpinバウンドに依存します。これは、ターゲットがUnpinを実装しているポインターでのみ呼び出すことができます。

あなたが見つけたと思った抜け穴が考慮されたので、 Rc<T: ?Unpin>からPin<Rc<T>>に行く方法がありません。 Rc::pinnedしてピンにRcを作成する必要があります。

@withoutboatsそれを確認したかったのですが、 T: Unpinは、無効になる前にdrop呼び出しをオプトアウトしています。 それは疑問を投げかけます、

fn into_inner(Pin<P>) -> P where P: Deref, P::Target: Unpin;

インターフェイスのどの部分も保護せず、 Pin<Box<T>>をBox<T>アンラップすることは潜在的に有用です(もちろんT: Unpinの場合)。

@HeroicKatoraあなたはその関数が安全であるということは正しいです。 追加してもかまいませんが、このスレッドはすでに数百のコメントの長さであるため、現時点で安定

Unpin命名と機能の両方が、その逆の方法ではるかに知覚された意味を持っていると言っておきます。 そして、 Pin保証が実際にT: !Unpinに制限されていることを知っています。 それは、可能性を示すためにエスケープハッチを構築する代わりに、そのようなメソッドの存在によっても直接示されます:smile:。 そして、そのドキュメントは、制限を説明する(またはもう一度リンクする)のに最適な場所です。 ( unpin代わりにinto_inner unpinという名前を付けることも検討できます)。

編集:それはほとんどすでにそこにあるものを一般化するでしょう。

impl<P> Pin<P> where P: Deref, P::Target: Unpin

  • fn unpin(self) -> P

P=&'a mut Tのインスタンス化があり、これは提案されたものと同等です

impl<'a, T: ?Sized> Pin<&'a mut T> where T: Unpin

  • fn get_mut(self) -> &'a mut T

安定化が完了したので、PFCPのポイントは議論の余地があるように思われるので、次のようになります。

@rfcbotキャンセル

https://github.com/rust-lang/rust/issues/55766#issuecomment-437374982以降に作成されたコメントがドキュメントに変換されることを確認するための追跡はありますか? ベータ版が分岐したため、リリースにはすでに遅すぎるようです...安定化の前に発生するようにここで明示的に呼び出されましたが:/

これがまだ開いている理由はありますか? @Centrilあなたはこれを閉じて再開しました、それは意図的でしたか?

@RalfJung FCPをキャンセルしようとしましたが、リッスンしませんでした。 @alexcrichton FCPをキャンセルできますか?

これは安定しているので、閉じます。

このページは役に立ちましたか?
0 / 5 - 0 評価