はじめに
スタックとかヒープとかについてうろおぼえだったので軽くスルーしながら Rust を勉強していったんですけど、ちゃんと理解しておくといろいろわかりやすくなっていくのでちゃんとやったほうがいいなーと思ったのでちょっとまとめてみました。
自分も勉強中なのでつっこみ有れば歓迎です。
スタックとは
プロセス起動時に割り当てられたメモリ領域です。
プロセスから見ると起動時からアドレスを知っているのでアクセスは速いのですが、容量はあまり大きくないです。
データの保存・取得方法に癖があり、スタックを配列と見立てると、保存時には push しかできず、取得時には pop しかできません。
不便ではありますが、末尾のアドレスさえ知っていればすぐに読み書きできるので、その構造からもアクセス速度は速くなります。
ヒープとは
必要に応じてプロセスに割り当てられるメモリ領域です。
割り当てにあたって OS にメモリー割り当ての要求をする必要があるので、はじめから割り当てられているスタックに比べるとアクセスが遅くなりますが、利用できる容量はスタックよりも大きくなります。
データの保存・取得は任意のアドレスへ行うことができます。
関数とスタック
Rust では関数を実行する際に利用する引数や変数などのデータをスタックに保存し、関数終了時にスタックから取り除きます。
実行中の関数から関数を呼び出した場合には、実行中の関数のデータの後ろに新しい関数のデータが push されます。
スタックは末尾のデータにしかアクセスできないという制約を持ちながらも高速でアクセスできるという利点があると説明しましたが、
このように利用すると実行中の関数からは常に必要なデータには高速にアクセスでき、関数が終了してデータを pop すると関数呼び出し前の状態に容易に復帰できるので都合が良いのです。
所有権システムとスタック
関数とスタックの関係を知っていると、所有権システムについて理解がしやすくなります。
所有権とは「値が変数に束縛され、変数の宣言されたスコープが無効になると値が破棄される」というルールのことですが、これは関数呼び出し終了でスタックからデータが破棄されるという挙動をそのまま抽象化したものであると気がつきます。
スタックとヒープと変数
前々節で変数のデータはスタックに保存されると説明しましたが、一部のデータはヒープに保存されます。
ヒープの特徴はアクセスが低速な代わりに任意の容量を割り当てることができると説明しましたが、その特徴の通り可変長のデータや容量の大きいデータはヒープに保存され、実データへのポインタやデータ長などのメタデータのみスタックに保存されます。
こうしてデータをヒープに置きポインタだけスタックに置くことで、大きなデータも柔軟に扱いつつスタックの解放のタイミングで関連するヒープのデータも安全に解放することができます。
具体的にヒープに保存されるデータは String や Box、Vec などの型のもので、ヒープとスタックの特徴について理解しておくとこれらの型をうまく使えるようになるのではないでしょうか。