Rust 言語では、メモリ管理は従来、低レベルプログラミングにおける最も困難な問題の一つと考えられていました。Rust が登場する前は、多くの言語が手動でメモリ管理を要求しており(C/C++のように)、これがメモリリークやデータ損傷を引き起こしていました。Rust は、Vec<T> のようなコレクションが、所有権と借用のシステムを通じてメモリの割り当て、再配置(リサイズ)、および解放のタイミングを制御する、自動的かつ安全なメモリ管理戦略を採用することで、これにアプローチしました。
問題 は、ほとんどの言語がアロケータ(GC)の詳細を過度に抽象化するか、プログラマにすべての責任を負わせる(malloc/free)ことです。動的配列の場合、メモリリークや配列の境界を超えること、所有権の侵害に注意することが極めて重要です。
解決策 は、Rust における安全な抽象化を通じた自動化です。Vec<T> はヒープ上にメモリを割り当て、サイズを動的に増加させ(通常は指数的に成長)、スコープを抜ける際にすべてを解放します(RAII)。
コード例:
fn main() { let mut v: Vec<i32> = Vec::new(); v.push(1); v.push(2); v.push(3); // 要素の追加はサイズの増加とメモリの再配置を引き起こす println!("Vector: {:?}", v); // main から出るとメモリは自動的に解放される }
主な特徴:
Vec<T> は事前にメモリを割り当て、必要に応じて再配置するVec への要素追加時の配列の成長の複雑さは何ですか?
通常、push の複雑さは ** amortized ** O(1) ですが、配列がオーバーフローすると、新しいメモリ領域が割り当てられ(サイズが約倍になります)、すべての要素がコピーされます。この時点が唯一の例外で、その操作は O(n) になります。
v[index] を通じて範囲外の要素を取得しようとした場合、どうなりますか?
角括弧を使用すると、範囲外アクセスが原因でパニックが発生します。安全にエラーを処理するためには、Option を返す .get() メソッドを使用する必要があります。
let element = v.get(10); // インデックスがない場合は None
Vec の要素への参照を使用しても、ベクターの成長(リサイズ)の可能性がある場合、使用できますか?
いいえ、ベクターがリサイズされると(たとえば、オーバーフロー時の push)、すべてのメモリが移動する可能性があり、古い参照は無効になります - コンパイルエラーが発生します(または、手動で使用する際に unsafe ブロック内で未定義の動作を引き起こす可能性があります)。
開発者が Vec<T> に基づいてメッセージキャッシュを実装し、要素への外部参照を提供します。新しい挿入の後、メモリの再配置が発生し、すべての既存の参照が「ダングリング」になります。その結果、アプリケーションがクラッシュします。
利点:
欠点:
要素の内部識別(インデックス/キー + 有効性のチェック)を使用するか、コピーメモリまたはイミュータブル値のみを返し、Vec の要素への長寿命参照を保持することは許可されません。
利点:
欠点: