Progrust Library.

// dictionary

クローン.

.clone()を呼んで値の独立した複製を明示的に作ることをクローンと呼びます。文字列型ベクタ(Vec)のようにヒープにデータを持つ型は、代入や受け渡しのたびにムーブが起きて所有権が移り、移動元の変数は使えなくなりますが、クローンを使えばムーブを避けて元の値を使い続けられます。この能力は標準ライブラリCloneトレイトが提供します。

fn main() {
    let order = String::from("コーヒー ×2");
    let cloned = order.clone(); // ヒープのデータごと複製(ムーブは起きない)

    println!("控え: {cloned}");
    println!("原本: {order}"); // OK: order の所有権は残っている
}
Playgroundで開く

Stringのようにデータ本体を複製する型では、クローン後の2つの値は完全に独立しており、片方を変更・破棄してももう片方には影響しません(複製の意味が異なる型もあります。末尾の補足を参照)。そのぶんヒープデータの複製という実行時コストがかかるため、読み取りだけなら参照借用するのが基本で、クローンは独立した複製が本当に必要な場面で使います。

Copyとの違い

コピーの仕組みとしてはCopyトレイトもあります(ムーブの「ムーブしない型」を参照)。両者は次のように役割が異なります1

観点 Copy Clone.clone()
起き方 代入や受け渡しで暗黙に起きる 明示的に呼び出す
コスト 軽量なビット単位コピーのみ 型ごとに自由(高コストになりうる)
実装する型の例 整数型などのスカラー型 StringVecなどヒープを使う型

なおCloneCopyのスーパートレイトであり、Copyを実装する型は必ずCloneも実装します。

補足

自作の型をクローン可能にする

自作の構造体などは、全フィールドがCloneを実装していれば#[derive(Clone)]を付けるだけでクローン可能になります。導出された実装は各フィールドのcloneを順に呼びます(ジェネリック型では型パラメータにClone境界が付きます)。

#[derive(Clone)]
struct Order {
    item: String,
    count: u32,
}

fn main() {
    let order = Order { item: String::from("コーヒー"), count: 2 };
    let cloned = order.clone();
    println!("{} ×{}", cloned.item, cloned.count);
}
Playgroundで開く
「複製」の意味は型によって異なる

.clone()が常にヒープデータの完全な複製(ディープコピー)を作るとは限りません。何を「複製」とするかは型ごとの実装次第です1

  • StringVecなど多くの型: ヒープデータごと複製した独立の値を作る
  • 参照&T: 同じ値を指す参照がもう1つできるだけ
  • RcArcのような参照カウント型: データ本体は複製されず、参照カウントが増えるだけ(複製後も同じデータを共有する)

Footnotes

  1. std公式ドキュメント — Trait Clone 2

この辞書が使われているページ

backlinks 4

  1. 辞書ダングリング参照
  2. 辞書ムーブ
  3. 動かして学ぶRustプログラミング問題集 › 第7章 所有権とムーブ
  4. 動かして学ぶRustプログラミング問題集 › 第8章 参照と借用