stackとheap
実行中のプログラムが使うメモリには、大まかに stack と heap があります。 ここでは厳密な実装詳細ではなく、数値計算コードを読むための直感を押さえます。
stack
stack は、関数呼び出しに伴って使われる領域です。
小さい固定サイズの値や、関数の局所変数の一部が置かれます。
関数を抜けると、その関数のために使われていたstack領域はまとめて解放されます。
一方で、stack の大きさには上限があります。
Linux の一般的な環境では ulimit -s が 8192 KB、つまり約8 MiB に
設定されていることがよくあります。ただし、この値はOS、distribution、
shell の resource limit、thread の設定によって変わります。
例えば、次のような値は小さく、固定サイズです。
let dt: f64 = 0.01;
let n_steps: usize = 1000;
let x: [f64; 3] = [0.0, 1.0, 2.0];
数値計算では、巨大な固定長配列を局所変数として作ることは避けます。
例えば、次のような配列は f64 が100万個なので約8 MBあります。
これは stack の上限に近く、環境によっては stack overflow の原因になります。
let values: [f64; 1_000_000] = [0.0; 1_000_000];
大きな配列は、実行時に動的に確保し、heap に置くのが基本です。
heap
heap は、実行時にサイズが決まるデータや、大きなデータを置くための領域です。 数値計算で使う大きな配列は、通常heapに置かれます。
let n = 1_000_000;
let values = vec![0.0_f64; n];
Vec<f64> そのものは、長さ、容量、heap上のデータへのポインタを持つ小さな値です。
実際の100万個の f64 はheap側に置かれます。
所有権との関係
Rustでは、heap上のデータを誰が所有しているかを型システムで追跡します。
これは、後の章で扱う Vec<f64>、slice、借用の理解につながります。
Vec<f64>はheap上のデータを所有する。&[f64]は所有せずに読む。&mut [f64]は所有せずに更新する。
この区別はRust固有の文法として現れますが、背景には 「大きなデータをどこに置き、誰が変更できるか」という一般的な問題があります。
第3章では、この考え方をRustの関数境界として扱います。