safe Rust は安全な言語と言われますが, 残念ながらスタックオーバーフローをコンパイル時に検出し防ぐことはできません. あるコードがスタックオーバーフローを起こすかどうかは環境に依存する話なので当然といえば当然ですが.
大きなデータを持つ
スタックサイズは OS 等にもよりますが数 MB 程度なので, それより大きな array をスタックに確保しようとすれば一発でオーバーフローします.
fn main() {
let _ = [ 0f64; 4_000_000 ];
}
解決策: 大きなデータはヒープに持つようにする.
関数の再帰呼び出し
スタックオーバーフローの一番多いと思われるパターンは関数の再帰呼び出しです. が, rustc は関数の再帰呼び出しが終了しないことに気づくと警告を出してくれます.
fn main() {
fn tmp() -> i32 { tmp() }
tmp();
}
これをコンパイルすると function cannot return without recursing
というメッセージとともに警告が表示されます. が, コンパイル自体は成功してバイナリを実行するとスタックオーバーフローします.
ただ, これよりちょっとだけ複雑な再帰呼び出しではコンパイラが気づかないこともあります.
fn main() {
fn a() -> i32 { b() }
fn b() -> i32 { a() }
a();
}
また, 再帰は理論上終了するものの, 終了するまでに必要な関数の呼び出し回数が多すぎるためスタックオーバーフローする, というケースもあります. これは末尾最適化により解決する可能性があります.
解決策: 設計ミスなので見直す. または末尾再帰最適化をする.