プログラミング
Valenのメモリ安全性:新しい種類の借用チェック
Valen's Memory Safety: A New Kind of Borrow Checking (verdagon.dev)
要約
この記事は、新しい柔軟な借用チェッカーを持つValen言語について紹介しています。著者は、Rustの借用チェックの強力さと、参照カウントやガベージコレクションのシンプルさ・柔軟性を併せ持つ「メモリ安全性の聖杯」を探求しており、Valenはそのための解決策を提供する可能性を示唆しています。特に、より緩和された借用チェッカー、複雑なデータ構造の扱いの改善、そしてそれらの連携に焦点を当て、グループ借用という概念を基盤としたアプローチを解説しています。
全文翻訳
言語 ∩ アーキテクチャ Github RSS Bluesky Mastodon r/valen Discord
Valenのメモリ安全性:新しい種類の借用チェック
2026年10月11日 — Evan Ovadia
この記事は、Valenの新しい柔軟な借用チェッカーについてです。
こんにちは!皆さんもご存知の通り、私は新しいメモリ安全性のアプローチを探求し、それらを奇妙な方法でブレンドするのが大好きです。これは、メモリ安全性の聖杯を見つけるという私の永遠の探求の一部です。それは、Rustの借用チェックほど強力でありながら、参照カウントやガベージコレクションほどシンプルで柔軟なものです。
0 1
私は長い間、聖杯を見つけるためには、3つのメモリ安全性に関する課題を解決する必要があると考えてきました。
1. より緩和された借用チェッカーを作れるか?
2. より複雑なデータを借用チェッカーで扱えるか?
3. それらの2つのシステムを同時に機能させることができるか?
曖昧で難解ですね!以下で、それが何を意味し、Valenがそれらのための解決策を見つけたかもしれない方法を説明します。
5
この記事は主に最初の部分、つまりValenのより緩和された借用チェッカーに焦点を当てています。したがって、この記事では、それが何であり、何ができ、どのように機能するかについて説明します。
この記事では、明確さのために構文糖衣(シンタックスシュガー)をいくつか使用しています。自動デリファレンス、構造体への直接インデックス(my_vec[0])、グループからの型推論(entity in world.entities[])は11月にリリースされる予定です。これらの構文調整を除けば、借用チェッカーは私たちが話しているすべてのことに対して機能します。
6
そして、その過程で、次のようなより奇妙な詳細についても説明します。
コンパイラが参照がどこを指しているかを記憶する方法。
Valenが、他の借用チェッカーが持つ2つではなく、1種類の借用参照を持つ方法。
Valenの借用チェッカーがRustコードの呼び出しを支援するように形成されている方法。
また、この記事は非常に長く、多くの文脈が含まれているため、どこをスキップすればよいかをお知らせします。
飛び込みましょう!
グループ借用:惜しいところと、巨大な可能性(これは私が話すのが大好きな単なる背景情報です。このセクションは自由にスキップしてください!)
Valenのアプローチは、私の友人であるニック・スミスが設計した「グループ借用」の提案に基づいています。それは、コンパイラが参照がどこを指しているかを記憶している(後で説明します)という中心的な概念に基づいており、その情報を使用してプログラムがメモリ安全であることをチェックします。
この設計の人生は困難なスタートを切りました。時間の流れの中で埋もれそうになっていました。元々はMojoのために設計されましたが、残念ながら、いくら努力しても、それを使用すべきだと上層部を説得できませんでした。
7
グループ借用は、成功する機会を得る前に死んでいました。しかし、それが世界に何とかして入り込めれば、巨大な可能性があることを知っていました。そのため、数ヶ月かけて説明を洗練させ、その利点を見つけ出し、昨年それについて記事を書きまし
8、他の言語が採用してくれることを願っていました。そして、彼らは採用しました!それは広く広まりました。
GoogleのCarbon言語は完全にグループ借用にピボットしました(そして私たちの記事を引用してくれました、それは良かったです!)。Anteはそれを彼らの言語に取り入れ始めました。共有ミュータビリティと安定性を参照してください。Plecraの排他型は、グループ借用の概念とGhostCellでRustの借用チェッカーを再設計しています。
10
Zetaはグループ借用と分離性(disjointness)をブレンドし
11、実行時事実をコンパイル時のメモリ安全性にファクタリングしました。Fangはそれを彼らの設計に取り入れています。Fangの循環参照を参照してください。
そして今、私のValenはグループ借用をValeのアプローチと組み合わせて、Rustコードとの相互運用性も実現しています。
これらの言語のほとんどは、同じような課題を巡っています。より柔軟な借用チェッカーをどのように作るかということです。例えば、Valenは参照カウントや世代参照と連携できるほど柔軟な借用チェッカーを作りたいと考えており、CarbonはC++コードと連携できるほど柔軟な借用チェッカーを作りたいと考えています。
これは困難です。なぜなら、借用チェックと他のアプローチの間には根本的な対立があったからです。それは次のように要約できます。
13
借用チェックには「共有排他ミュータブル」の制限があります。オブジェクトへの参照を保持している場合、他の誰もオブジェクトを変更できません。
他のアプローチでは、オブジェクトへの参照を保持しながら、他の人にそれを変更させたいと考えています。
14 15
おそらくあなたは、「これを解決する方法は絶対にない」と考えているでしょう。そのように見えます!しかし、グループ借用は実際にはこの対立を解決します。「共有排他ミュータブル」を「解放後使用(use-after-free)」なしに緩和することによってです。
それは難解で、後でグループ借用がどのように機能するかを説明するときに意味をなすでしょう。
したがって、グループ借用が何ができるかを見て、次にそれがどのように機能するかを説明しましょう。
グループ借用ができること
グループ借用は、実行時コストなしでプログラムにメモリ安全性を提供します。
16
解放後使用(use-after-free)の問題をコンパイル時に捕捉し、以前のアプローチよりも制限が少ない方法で行います。
解放後使用エラーがコンパイル時に捕捉される例を次に示します。
valen struct Entity {
hp int;
}
struct World {
entities Vec<Entity>;
}
func main() int {
let world = World(Vec<Entity>.new());
world.entities.push(Entity(42));
let first_ref = &world.entities[0];
world.entities = Vec<Entity>.new(); // コンパイルエラー:無効化された借用を使用しました
return first_ref.hp;
}
これは異常に柔軟なアプローチです。通常、コンパイラは以下のプログラムで苦労しますが、このアプローチはそれが安全であることを理解しています。
17
valen func main() int {
let list = Vec<int>.new();
// リストへの2つの参照を作成します
let ref_a = &list;
let ref_b = &list;
// 両方の参照を通してリストを変更します
ref_a.push(42);
ref_b.push(73);
return 42;
}
グループ借用は、借用チェッカーにとって通常困難な多くのパターンを受け入れます。例えば:
オブジェクトを指す複数のローカル変数があり、それらのいずれかを通して書き込む(上記参照)。
オブジェクトを指すパラメータと、そのオブジェクト内のどこかを指す別のパラメータを取り、後者を通してのみ書き込む(次のセクションで確認します)。
同じオブジェクトを指す複数の関数パラメータがあり、それらのいずれかを通して書き込む。
18
スコープを外れるときにオブジェクトを変更する「ロールバック」構造体を作成する(ScopeGuardパターンと同様)。
19 20
共通のサブシステムへのミュータブル参照を持つ複数の構造体。
21
さらに多くのことを、以下で説明します。
22
これらはC++から愛されているパターンですが、コンパイル時にメモリ安全な方法でこれらすべてを実行する方法をどの言語もまだ見つけていません。
サイドノート(興味深い周辺的な考え)
ノート [–]
ノート[+]
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
0
この探求は、制約参照、線形型、参照カウントなど、多くの興味深い実験につながりました。
1
この記事の0%はAIによって書かれました。詳細はこちら。私のスタンス:誰かに何かを読む時間を取ってもらいたいなら、自分で書く時間を取るべきです。読んでくれてありがとう=)
2
より具体的には、「1つのオブジェクトに対する複数の読み取り/書き込み借用参照をサポートする借用チェッカーを作成できるか?」ということです。この記事ではそれを完全に説明しているので、読み続けてください!
3
これは後で詳しく説明しますが、「より複雑なデータ」とは、オブジェクトが他のオブジェクトへの参照を自由に持つことができるデータのグラフ全体を意味します。C++やJavaなどに見られるような、制限はありません。RustやFortranのように、一般的にオブジェクトが他のオブジェクトへの参照を持つことができないものとは対照的です。したがって、「より複雑なデータを読み取る」と言うとき、私は次のような意味です。その種の任意のデータグラフを指す不変の借用参照をどのように持つことができますか?(注:Rust構造体は他のRust構造体への参照を持つことができますが、一時的にのみです。これがECSがRustゲームで人気のある選択肢である理由です。そのため、Rustを依然としてFortran側により近いものと数えています。)
4
要するに、これはValeの世代参照と領域借用のブレンドによって解決されます。その不変の領域借用は、コンパイラが既存のすべてのデータを一時的に凍結された「領域」として追跡できるようにし、その領域への借用参照を持つことができます。
5
今のところ、ValenはValeのアプローチ、2023年の特定の思考実験、ニック・スミスのグループ借用アプローチ、そして薄くするためにいくつかのひねりをブレンドすることによって、これらを解決したかもしれないと示唆するだけです。