C/C++やその他の低レベル言語では、開発者はメモリ内のデータの配置を明示的に管理する必要があります:スタック(自動変数)またはヒープ(malloc/newを介しての割り当て)。これらの言語では、メモリリーク、ダブルフリー、または未初期化のメモリやすでに解放されたメモリの使用に関するエラーが頻繁に発生します。Rustは、ガベージコレクタを使用せずに、所有権システムによってメモリを厳格に管理する責任を負います。
スタックによる自動メモリ管理は便利ですが、サイズ(スタックの深さ)に制限があります。ヒープの割り当てにはリソースの明示的な管理が必要であり、これは危険です。メモリを解放するのを忘れたり、ポインタのライフタイムを破る可能性があります。ガベージコレクタは常に解決策ではありません(リソースコスト、予測不能な一時停止)。メモリ管理のエラーは、クラッシュや脆弱性につながります。
Rustでは、スタックとヒープは自動管理によって区別されます:すべての値はデフォルトでスタックに配置され、動的にサイズが決まるか、長生きするオブジェクトにはスマートポインタ(例えば、Box<T>、Vec<T>)を介してヒープが使用されます。所有権と借用のシステムは、所有権の移転やライフタイムの終了後にリソースが自動的に解放されることを保証します。これにより、コンパイル時の安全性が保証され、ガベージコレクタによる余分な一時停止が排除されます。
コード例:
fn main() { let a = 42; // スタックの割り当て let b = Box::new(42); // ヒープの割り当て let mut v = Vec::new(); v.push(1); v.push(2); // ヒープの配列データ }
主な特徴:
スタックのメモリを手動で解放(drop)できますか?
いいえ。スタックに割り当てられた変数の解放は、スコープを出ると自動的に行われます。手動でdropを行うことは無駄であり、スタックポインタに対しては許可されていません。
移動(move)はヒープに移動を引き起こしますか?
いいえ。所有者間での変数の移動は、必ずしもヒープに移動するわけではなく、所有権のみが変更されます。
Box<T>の使用はTが常にヒープにあることを保証しますか?
はい、Box<T>は確かにTをヒープに割り当てますが、所有権とライフタイムは依然として厳格に管理されています。
プロジェクトでは、手動でメモリを解放するためにmem::forgetやdropを使用したグローバルベクター(Vec<T>)を使用しており、時には解放されたメモリへのダングリングポインタを残しています。
利点:
欠点:
オブジェクトは明示的にBoxを介して配置され、データは所有権のルールに従って渡され、コレクションにはスマートポインタを使用し、スタック変数へのリファレンスを返しません。
利点:
欠点: