// dictionary
ムーブ.
Rustでは以下のようにヒープに格納された値を持つ変数をほかの変数へコピーできます。このコピーではヒープメモリ上のデータは複製されません。
let order = String::from("コーヒー ×2");
let confirmed = order;
このコピーではその所有権も同時に渡されます。「所有者は常にただ1つ」というルールを保つため、所有権がない変数は無効となり、その後の利用はできなくなります。
そのため、このようなコピーをRustでは「ムーブ(move)」と呼びます。
fn main() {
let order = String::from("コーヒー ×2");
let confirmed = order; // 所有権が order から confirmed へムーブ
println!("{confirmed}"); // OK: 今の所有者は confirmed
println!("{order}"); // エラー: E0382(ムーブ済みの値の使用)
}Playgroundで開くムーブが起きる場面
ムーブを起こすための特別な操作は基本的に不要で、値の受け渡しの際に自動で発生します。
| 場面 | 例 | 所有権の移動先 |
|---|---|---|
| 変数への代入 | let b = a; |
b |
| 関数の引数への受け渡し | f(a) |
関数の仮引数 |
| 関数からの戻り値 | let b = f(); |
受け取る側の変数 b |
ムーブしない型(Copy型)
整数型や論理値型のようなスカラー型などはCopyトレイトを実装しており、代入や受け渡しのたびに値そのものがコピーされるため、ムーブは起きません(移動元の変数もそのまま使えます)。一方、文字列型やベクタ(Vec)のようにヒープ領域の解放という後始末を担う型はCopyを実装できないため、ムーブが起きます。
補足
ムーブの実体は浅いコピー
文字列型をムーブしても、ヒープ上の文字列データ本体はコピーされません。移動するのはスタック上の管理情報(ヒープへのポインタ・長さ・容量)だけで、そのうえで移動元の変数が無効化されます。The Rust Referenceでは、ローカル変数からムーブした後のその場所は「未初期化(deinitialized)」となり、再代入で初期化し直すまで読み取れないと規定されています1。移動元を無効化することで、同じヒープ領域を2つの変数が指し続けること(二重解放の原因)を防いでいます。
ムーブさせずに値を使いたい場合
所有権を渡したくない場合は、参照で値を「借りる」(借用)のが基本です。また、独立した複製が必要なら.clone()によってクローンすることでヒープのデータごとコピーできます(そのぶん実行時コストがかかります)。
Footnotes
この辞書が使われているページ
backlinks 8