Rustコトハジメ

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

Copy trait and its applications

What is Copy trait

Copy trait is a marker trait that claims the data is able to copy instead of move. So types that implement Copy trait have copy semantics instead of the default move semantics.

pub trait Copy : Clone

What conditions should Copy trait satisfy

To implement Copy trait the type must met the following conditions

  • The members are all Copy
  • It is safe to copy ... (1)
    • For instance, mutable reference is not able to implement Copy because it violates the borrow checker
  • It doesn't implement Drop ... (2)
    • This is to avoid double free

As for (1), very interesting fact is that &mut T in not Copy while &T is Copy.

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

This means assignment of &[T] copies but &mut [T] moves.

As for (2), copying smart pointers like Vec<T> or String leads to so-called double free because each copy tries to free the single heap allocated area when it goes out of scope.

Here is my article about smart pointers. The smart pointers mentioned in the article are all not Copy. Please read if you like to know more

www.rustforbeginners.com

The applications of Copy trait

There are functions that optimizes on speed by exploiting the fact the data is able to directly copy on memory.

The copy_from_slice function of Vec is a faster version of clone_from_slice because it copies Ts memory area directly.

    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 has implementation much similar to this but does not require Copy for T because the total amount of ownership will preserve after execution.

    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());
        }
    }

One more thing to mention is that Extend trait use so-called specialization when T is 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())
    }
}