Rustの世界へようこそ!今回はRustの最もユニークで重要な機能の一つである所有権について学びます。特に、値がどのように変数間で移動するのか、Move(ムーブ)とCopy(コピー)の違いに焦点を当てていきましょう。
所有権は、Rustがガベージコレクタなしでメモリ安全性を保証するための仕組みです。最初は少し難しく感じるかもしれませんが、理解すれば安全で効率的なコードを書くための強力な武器になります💪
所有権のルール
Rustの所有権には、以下の3つの基本的なルールがあります。
- それぞれの値は、所有者と呼ばれる変数を持っています。
- 一度に存在できる所有者は、ただ1つだけです。
- 所有者がスコープ(変数が有効な範囲)から外れると、値は自動的に破棄(メモリ解放)されます。
これらのルールによって、メモリリークやダングリングポインタといった問題をコンパイル時に防ぐことができます。
Move(ムーブ) – 所有権の移動 🚚
Rustでは、多くの型で、値を別の変数に代入すると所有権が移動(Move)します。これは特に、ヒープ領域にデータを格納する型(例: String
)で重要になります。
例を見てみましょう:
let s1 = String::from("hello"); // s1が"hello"の所有者
let s2 = s1; // s1の所有権がs2にMoveする
// println!("s1 is: {}", s1); // この行はコンパイルエラー! s1はもはや無効
上のコードでは、s1
が持っていたString
値の所有権がs2
に移動しました。Moveが発生した後、元の変数s1
は無効になり、アクセスしようとするとコンパイルエラーになります。これは、二重解放(同じメモリ領域を2回解放しようとすること)を防ぐためです。
Moveは、ヒープに割り当てられたデータの所有権を効率的に移転する方法です。データのコピーが発生しないため、パフォーマンス上の利点があります。
Copy(コピー) – 値の複製 🐑
一方、整数型 (i32
, u64
など)、ブール型 (bool
)、浮動小数点数型 (f64
)、文字型 (char
) のような、サイズが既知でスタック上に完全に格納される型は、代入時に値がコピー(Copy)されます。
これらの型はCopy
トレイトを実装しています。Copy
トレイトが実装されている型では、代入後も元の変数は有効なままです。
例を見てみましょう:
let x = 5; // xは5の所有者
let y = x; // xの値がyにCopyされる
println!("x = {}, y = {}", x, y); // これはOK! xもyも有効
この場合、x
の値5
がy
にコピーされます。x
は依然として有効であり、5
という値を持っています。これは、スタック上のデータのコピーは非常に高速に行えるためです。
Copy
トレイトを持つ型は、暗黙的にClone
トレイトも実装しています。Clone
は明示的に深いコピーを行うためのトレイトです。
MoveとCopyの挙動まとめ
挙動 | 説明 | 対象となる主な型 | 代入後の元の変数 |
---|---|---|---|
Move | 所有権が移動する。ヒープ上のデータのコピーは発生しない。 | String , Vec<T> , Box<T> など (Copy トレイトを実装していない型) | 無効 |
Copy | 値が複製される。スタック上のデータが対象。 | 整数型 (i32 , u64 等), ブール型 (bool ), 浮動小数点数型 (f32 , f64 ), 文字型 (char ), 要素がCopyなタプルなど (Copy トレイトを実装している型) | 有効 |
まとめ ✨
今回はRustの所有権の基本、特にMoveとCopyの挙動について学びました。
- Moveは所有権を移し、元の変数を無効にします(主にヒープ上のデータ)。
- Copyは値を複製し、元の変数も有効なままです(スタック上のデータ、
Copy
トレイト実装型)。
所有権システムは、Rustがメモリ安全性をコンパイル時に保証するための核心的な仕組みです。この概念をしっかり理解することが、Rustプログラミングの第一歩となります。
次回は、所有権を移動させずに値を使いたい場合に役立つ「借用」について学んでいきましょう!➡️
コメント