【Rust】Clone トレイト

blog1.mammb.com


std::clone::Cloneトレイト

std::clone::Clone トレイトを実装した値は、自身のクローンをメモリ上に新たに作ることができる。

以下のような定義となっている。

pub trait Clone: Sized {
    fn clone(&self) -> Self;
    fn clone_from(&mut self, source: &Self) {
        *self = source.clone()
    }
}

clone() メソッドは、self の独立したコピーを生成し、clone_from() メソッドは、self を書き換えて source のコピーにする。

Clone トレイとは Sized マーカートレイトを拡張しているため、メモリ上におけるサイズが既知な型だけがクローン可能となる。

blog1.mammb.com


clone() メソッド

標準ライブラリで定義される型は、コピーして意味のあるものほぼ全てが Clone を実装している。

独自の型に Clone を実装する場合、型定義の上に #[derive(Clone)] 属性を書くことで、Rust が自動的に Cloneトレイトを実装してくれる。

#[derive(Clone, Debug)]
struct Reading<T> {
    frequency: T,
}

型変数 T はデフォルトで struct Reading<T: Sized> と解釈されるため、clone() により芋づる式に値がコピーされ、別の資源として値を取得できる。

let original = Reading {frequency: "xxx"};
let cloned = original.clone();

drop(original);
println!("clone: {:?}", cloned);

clone() はその型が保持する全ての値をコピーすることになるため、高価な処理になる可能性があるため注意して使う必要がある。


clone_from() メソッド

a = b.clone()a.clone_from(&b) と書くことができる。機能的にはどちらも同じだが、a.clone_from(&b) はリソースを再利用するためのオーバーライドが提供されている可能性がある。

デフォルトの実装では *self = source.clone() のように source をクローンして *self に移動しているだけであるが、例えば String では以下のように最適化されたオーバーライドが提供されている。

impl Clone for String {
    fn clone(&self) -> Self {
        String { vec: self.vec.clone() }
    }

    /// Clones the contents of `source` into `self`.
    ///
    /// This method is preferred over simply assigning `source.clone()` to `self`,
    /// as it avoids reallocation if possible.
    fn clone_from(&mut self, source: &Self) {
        self.vec.clone_from(&source.vec);
    }
}

String の内部ベクタが、クローン元の内容を保持するのに十分なサイズが確保されていた場合、確保済みのバッファの中身を書き換えるだけでよく、これにより余分なヒープ確保や開放が不要となる。