プログラムメモリのレビュー

プログラムは 2 つの方法でメモリを割り当てます:

  • スタック: ローカル変数のための連続したメモリ領域。

    • 値のサイズは固定で、コンパイル時にわかります。
    • 非常に高速: スタックポインタを動かすだけです。
    • 管理が容易: 関数呼び出しに従います。
    • メモリ局所性が高いです。
  • ヒープ: 関数呼び出しの外にある値を格納する領域。

    • 値のサイズは動的で、実行時に決まります。
    • スタックよりわずかに低速: 多少の管理処理が必要です。
    • メモリ局所性は保証されません。

String を作成すると、固定サイズのメタデータはスタックに、動的なサイズの データ、つまり実際の文字列はヒープに配置されます:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let s1 = String::from("Hello");
}
StackHeaps1capacity5ptrHellolen5
  • String は内部的に Vec によって実装されているため、capacity と length を持ち、 可変であれば、ヒープ上での再割り当てによって拡張できることに触れてください。

  • 学生からそれについて質問があれば、基盤となるメモリは System Allocator を使用してヒープに 割り当てられており、カスタムアロケータは Allocator API を使って 実装できることを説明できます

さらに詳しく

unsafe Rust を使うとメモリレイアウトを調べられます。ただし、これが当然 unsafe であることは指摘しておくべきです!

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let mut s1 = String::from("Hello");
    s1.push(' ');
    s1.push_str("world");
    // 家でこれを試さないでください!教育目的のみです。
    // String はそのレイアウトについて何の保証もしないため、これにより
    // 未定義動作が発生する可能性があります。
    unsafe {
        let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
        println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
    }
}