Rustコトハジメ

プログラミング言語Rustと競プロに関する情報をお届けします。

Copyトレイトとその応用例

Copyトレイトとは

Copyトレイトは、その値をまるまるコピー出来ることを表すマーカートレイトである。Copyトレイトを実装するとデフォルトのムーブからコピーになる。

pub trait Copy : Clone

Copyトレイトが満たすべき条件

Copyトレイトを実装するためには以下を満たす必要がある

  • メンバーがすべてCopyトレイトを実装している
  • 安全にコピー出来ること ... (1)
    • 例えばmutable referenceはコピー出来ない。ボローチェッカーに違反するため
  • Dropを実装していないこと ... (2)
    • ダブルフリーを起こさないため

(1)について興味深い事実は、&mut TはCopyではないが、&TはCopyだということである。

// Shared references can be copied, but mutable references *cannot*!
impl<T: ?Sized> Copy for &T {}

つまり、&[T]の代入はコピーされるが、&mut [T]の代入はムーブされる。

(2)については、Vec<T>Stringのスマートポインタをコピーしてしまうと、各々がスコープを抜ける時に自身が管理するヒープ領域を解放しようとするから、ダブルフリーとなる。

スマートポインタについては過去に書いた。ここに書いてあるスマートポインタはすべて、Copyでは「ない」。

www.rustforbeginners.com

Copyトレイトの応用例

Copyトレイトを実装したものは安全にコピーすることが出来るという事実を利用して、高速化を行っている実装が存在する。

Veccopy_from_sliceclone_from_sliceの高速バージョンであるが、TがCopyである事実を活かしてメモリ領域をまるまるコピーしている。仮にTが所有権を管理している場合、これは所有権の複製を行うから実現出来ない。

    pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy {
        assert_eq!(self.len(), src.len(),
                   "destination and source slices have different lengths");
        unsafe {
            ptr::copy_nonoverlapping(
                src.as_ptr(), self.as_mut_ptr(), self.len());
        }
    }

実装が似ている関数としてはswap_with_sliceがあるが、これはスワップ後にも所有権が保存されるためCopyを要求しない。

    pub fn swap_with_slice(&mut self, other: &mut [T]) {
        assert!(self.len() == other.len(),
                "destination and source slices have different lengths");
        unsafe {
            ptr::swap_nonoverlapping(
                self.as_mut_ptr(), other.as_mut_ptr(), self.len());
        }
    }

その他、Extendトレイトでは、TがCopyである場合に対して特殊化を利用した高速化を行っている。

/// Extend implementation that copies elements out of references before pushing them onto the Vec.
///
/// This implementation is specialized for slice iterators, where it uses [`copy_from_slice`] to
/// append the entire slice at once.
///
/// [`copy_from_slice`]: ../../std/primitive.slice.html#method.copy_from_slice
#[stable(feature = "extend_ref", since = "1.2.0")]
impl<'a, T: 'a + Copy> Extend<&'a T> for Vec<T> {
    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
        self.spec_extend(iter.into_iter())
    }
}