本文へスキップ
BecomeCoder

Rustコース · 第3章 メモリの土台 ― スタックとヒープ · レッスン13

値はどこに置かれるか ― Copy 型とそうでない型

ブラウザで完結

導入

同じ「変数」でも、スタックだけで完結する値と、ヒープを使う値では、Rust での振る舞いがまったく変わります。この違いが、次章の「ムーブ」と「コピー」の分かれ道になります。まずは完全にスタックで完結する型から。

説明

i32boolchar、それらだけからなるタプルや固定長配列は、大きさがコンパイル時に決まっているので、まるごとスタックに置かれます。こうした型を代入すると、値はまるごと複製(コピー) されます。

fn main() {
    let a = 5;
    let b = a;      // a の値(5)がコピーされる。a も b も別々に 5 を持つ

    println!("a = {a}, b = {b}");   // どちらも使える → a = 5, b = 5
}

b = a で「5」というスタック上の値がそっくり複製されます。ab は独立した2つの 5 です。だから代入後も両方そのまま使えます。図にするとこうです。

graph TD
    subgraph stack["スタック"]
        a["a: 5"]
        b["b: 5(a とは別の複製)"]
    end

このように「代入や関数渡しで自動的に丸ごと複製される」型を、Rust では Copy(Copy トレイトを持つ型)と呼びます。スタックだけで完結し、複製が安くて安全だからです。

  • Copy になる代表:i32/u32/f64 などの数値、boolcharCopy な型だけのタプル(例:(i32, i32))、固定長配列。
  • Copy にならない代表StringVec<T> など、ヒープを使う型。これらは丸ごと複製すると高コスト、かつ「同じヒープを2人が持つ」危険があるため、単純コピーはしません。ここで次のレッスンの String が登場します。

つまり Rust では、「その型がヒープを持つかどうか」で代入時の挙動が変わります。スタック完結型は気軽にコピー、ヒープを持つ型は特別扱い——この線引きが所有権のすべての出発点です。

試すには

let a = 5; let b = a; println!("{a}"); は問題なく動きます。次のレッスンで、これを String に置き換えると挙動が一変することを確認します。「数値だと平気なのに、なぜ文字列だとダメなのか」を意識しておいてください。

実際に動かしてみよう

下のエディタにRustを書いて「実行」を押すと、学習用シミュレータが println! の出力を表示します(本物のrustcではなく、教材の範囲を再現した軽量エンジンです)。struct・enum・match・トレイト・Vec/String/HashMap・Option/Result なども動きます。本文の例を書き換えて動かしてみましょう(所有権・借用チェッカーのコンパイルエラーは再現しないため、動きの確認用として使ってください)。

Rust — ブラウザ内で実行(学習用シミュレータ)

Rustの教材サブセットを動かす学習用シミュレータを読み込みます(本物のrustcではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。