// 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()) |
|---|---|---|
| 起き方 | 代入や受け渡しで暗黙に起きる | 明示的に呼び出す |
| コスト | 軽量なビット単位コピーのみ | 型ごとに自由(高コストになりうる) |
| 実装する型の例 | 整数型などのスカラー型 | String・Vecなどヒープを使う型 |
なおCloneはCopyのスーパートレイトであり、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。
StringやVecなど多くの型: ヒープデータごと複製した独立の値を作る- 参照
&T: 同じ値を指す参照がもう1つできるだけ RcやArcのような参照カウント型: データ本体は複製されず、参照カウントが増えるだけ(複製後も同じデータを共有する)
Footnotes
この辞書が使われているページ
backlinks 4