プログラミング
Rustプロジェクトの目標:移動不能な型と保証されたデストラクタ
Rust project goals: Immobile types and guaranteed destructors (github.com)
要約
Rust言語の将来的な開発目標として、型の移動(メモリ上での再配置)や破棄(メモリからの解放)に関する挙動をより明示的に制御できるようにする提案です。これにより、`Pin`による複雑さを軽減し、安全なスコープ付き非同期タスクの生成などを可能にすることを目指しています。具体的には、`Move`や`Forget`といった新しいトレイトを導入し、型の能力を明示化するアプローチが取られます。
全文翻訳
移動不能な型と保証されたデストラクタ
メタデータ
担当者 @lcnr
ステータス 受理済み
何と、そしてなぜか
型が移動または忘れられる(forget)ことをオプトアウトできるようにし、スコープ付きスポーン、非同期ドロップ、ピンバイデフォルトを可能にする。
期間
2026-2027
ロードマップ
非同期の追加のみ
ロードマップ
Linux向けRust
トラッキング課題 [#635]
その他のトラッキング課題
[rust-lang/rust#149607]
Zulipチャンネル
#t-lang/move-trait
[型]チャンピオン @lcnr
[言語]チャンピオン @jackh726
概要
型に対してどのような操作が可能かを記述する新しいトレイトを導入することを提案します。
現在、Rustはすべての型が移動可能(メモリ上で再配置可能)であり、忘れられる(forget、デストラクタを実行せずに)と仮定しています。私たちは、`Move`や`Forget`のようなトレイトを導入し、これらの機能を明示的にすることで、型がオプトアウトできるようにします。
これは、すべての型がコンパイル時にサイズがわかると仮定する`Sized`階層の作業と同様の先例に従っています。
コンパイラでMVPを実装し、RFCを書き、Linuxカーネルでの実世界テストを通じて実現可能性を検証します。
動機
現状
Rustは歴史的に、すべての値が移動可能(メモリ上で再配置可能)であり、忘れられる(mem::forgetを介して、デストラクタを実行せずに)と仮定してきました。
これらの仮定は言語に組み込まれています:代入は値を移動させ、mem::forgetは安全です。
しかし、一部の型はこれらの機能からオプトアウトする必要があります。
移動不能な型:多くの非同期Futureは自己参照型を望みますが、自己参照型は安全に移動できません。
現在の解決策は`Pin`ですが、これは場所のプロパティとして移動不能性をエンコードします。これにより、かなりの複雑さが生じます。
「安全なピン初期化問題」が説明しているように、`Pin`はLinuxカーネルのようなシステムで自己参照型を安全にエンコードするのに苦労しています。
保証されたデストラクタ:一部の型はデストラクタを実行する必要があります。
`Transaction`型は、クリーンアップの前に`commit()`または`rollback()`を必要とするかもしれません。
`scoped task handle`は、スコープを抜ける前に`join`する必要があります。
しかし、`mem::forget`は安全なので、Rustはデストラクタが実行されることを保証できません。
これは、非同期のための安全なスコープ付きスポーンのようなパターンをブロックします。
提案すること
新しいauto-traitを導入してRustの型システムを一般化し、型に対して可能な操作を記述します。
フレームワークは肯定的です:トレイトは能力を表します。
ベースレイヤーでは、型は特別な能力を持たないかもしれません。その後、必要なものをレイヤー化します。
Move:型はメモリ上で再配置可能である。
Destruct:型は暗黙的にドロップ可能である(スコープを抜けるときにデストラクタが実行される)。
Forget:型はデストラクタを実行せずにmem::forgetを介して忘れられることができる。
これは、`Sized`階層の作業と同様の先例に従っています。
その作業がスケーラブルなベクトルをサポートするために「すべての型はコンパイル時にサイズがわかる」という仮定を緩和したように、この作業は「すべての型は移動可能」および「すべての型は忘れられる」という仮定を緩和します。
`Move`トレイトは、移動可能性を場所のプロパティではなく型のプロパティとしてエンコードします。
`#[lang = "move"] unsafe auto trait Move {}`
`!Move`を実装する型は移動できず、その存続期間中は安定したアドレスを維持する必要があります。
これは`Pin`よりもシンプルです。なぜなら、移動不能性は型のプロパティであり、場所のプロパティではないからです。
`!Move`型の構築は、#t-lang/in-place-initからの作業に依存します。
`Forget`トレイトは、型が忘れられることをオプトアウトできるようにします。
`// Types implementing !Forget must have their destructors run`
`unsafe impl !Forget for ScopedTaskHandle {}`
`!Forget`を使用すると、安全なスコープ付きスポーンを構築できます。
ハンドルが忘れられないため、ハンドルのデストラクタがタスクをジョインし、ジョインが保証されます。
これは、現在安全なRustでは不可能なパターンを可能にします。
来年の作業項目
Moveトレイト
型がメモリ上での再配置をオプトアウトできるようにし、移動不能性を場所のプロパティではなく型のプロパティとしてエンコードする。
タスクオーナー(s)
ノート
Moveのコンパイラ実装
@lcnr および @nia-e
MoveのRFCを作成
@yoshuawuyts
Linuxカーネルでのテスト
@BennoLossin
RfLは、多くの自己参照データ構造を使用する重要なRustユーザーです。
Iteratorと!Moveの間の相互作用のテスト
@yoshuawuyts
ジェネレータベースのエフェクトをimpl Trait + !Moveにデシュガーして、自己参照をサポートできるようにすることを証明することが重要です。
保証されたデストラクタ
型がmem::forgetをオプトアウトできるようにし、安全なスコープ付きスポーンのようなパターンを可能にする。
タスクオーナー(s)
ノート
保証されたデストラクタの設計探索
@nikomatsakis
トレイト階層のオプションと既存の機能との相互作用を検討する
今年具体的に範囲外なのは、Futureトレイトの変更や更新に関連することすべてです。
これはRustで唯一安定したトレイトであり、Pinに依存しており、Moveを使用できるようにするための移行ストーリーが必要になります。
しかし、Pinに依存していることはFutureトレイトの唯一の欠点ではありません(1 + 2 + 10以上の問題)、そのためFutureトレイトの修正は独立したプロジェクトとして扱うのが最善です。
チームの要望
チーム
サポートレベル
ノート
[言語]
大規模な設計セッションが必要
[型]
実装+レビューに深く関与
よくある質問
これはSized階層の作業とどう関係しますか?
Sized階層の作業はパターンを確立します:Rustは、型がオプトアウトできるトレイト階層を導入することで、以前は普遍的だった仮定を緩和できます。
その作業は、スケーラブルなベクトルや外部型をサポートするために「すべての型はコンパイル時にサイズがわかる」という仮定を緩和します。
この目標は、同じパターンを「すべての型は移動可能」および「すべての型は忘れられる」に適用します。
これは「ピンの人間工学」イニシアチブとどう関係しますか?
この作業は、プロジェクト目標2025H2:「ピンの人間工学に関する実験の継続」の代替案です。これには以下の拡張が含まれます。
lvaluesの新しいアイテムファミリー`pin`、例:`&pin x`、`&pin mut x`、`&pin const x`。
Rustの`Drop`トレイトのワンオフオーバーロード、例:`fn drop(&pin mut self)`。
パターンにおける新しいアイテム種別`pin`、例:`&pin <pat>`。
特に、この作業はピンの重複定義問題を解決しません。つまり、これらの拡張があっても、既存のトレイトの`Trait`と`PinnedTrait`のバリアントに行き着くことになります。
`Drop`トレイトは例外であり、イニシアチブはワンオフオーバーロードを使用してそれを特別扱いすることを提案しています。
Pinを機能させるために言語を変更しようとするのではなく、問題はPinにあると考えており、移動不能な型がRustでエンコードされる方法を改善すべきだと考えています。
最終的な目標は、RustのPinを完全に非推奨にすることです。
Rustは永遠に後方互換性を約束するため、`&`や`mut`と同等の言語アイテムとして`pin`をサポートし続ける必要があります。
最終的な目標がPinを非推奨にすることであるため、Pinを言語の一部にすべきではないと考えています。
だからこそ、Moveは単なる補完的な提案ではなく、代替案として意図されています。
安全なスコープ付きスポーンを可能にするものは何ですか?
安全なスコープ付きスポーンには保証されたデストラクタが必要です。
パターン:スポーンは、タスクをジョインするデストラクタを持つハンドルを返します。
ハンドルを`mem::forget`できた場合、タスクはスコープを生き延びてダングリング参照にアクセスする可能性があります。
`!Forget`を使用すると、ハンドルのデストラクタは確実に実行され、パターンが安全になります。
これは、この目標の保証されたデストラクタ部分の主要な動機の一つです。
この設計空間についてもっとどこで読めますか?
いくつかのブログ記事がこの分野を探求しています。
Move, Destruct, Leakは、デストラクタと忘却のためのトレイト階層を探求しています。この投稿とは異なり、MoveはDestructのスーパートレイトであるべきではないと考えています。破壊/ドロップできるが移動できない型(例:クリーンアップを必要とする自己参照型)を持つことは有用です。
Must move typesは、呼び出し元に特定の操作を実行させる型という概念を導入しています。
Ergonomic Self-Referential Types for Rustとそのフォローアップは、Moveトレイトの設計を探求しています。
Why Pin is a part of trait signaturesは、この作業の動機となるPinの問題を説明しています。
Placing functionsは、`!Move`型をインプレースで構築するための構文を提案しています。