Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Клонування та копіювання

Під час роботи з ресурсами поведінка за замовчуванням — передавати їх під час присвоювань або викликів функцій. Однак іноді нам також потрібно зробити копію ресурсу.

Трейт Clone допомагає нам зробити саме це. Найчастіше ми можемо використати метод .clone(), визначений трейтом Clone.

Copy: неявне клонування

Трейт Copy дозволяє типу бути продубльованим простим копіюванням бітів, без потреби в додатковій логіці. Коли тип реалізує Copy, присвоювання та виклики функцій неявно копіюватимуть значення замість його переміщення.

Важливо: Copy вимагає Clone — будь-який тип, що реалізує Copy, має також реалізовувати Clone. Це тому, що Copy визначено як підтрейт: trait Copy: Clone {}. Реалізація Clone для типів Copy просто копіює біти.

Не всі типи можуть реалізувати Copy. Тип може бути Copy лише якщо:

  • Усі його компоненти є Copy
  • Він не керує зовнішніми ресурсами (такими як пам’ять у купі, файлові дескриптори тощо)
// Unit-структура без ресурсів
// Примітка: Copy вимагає Clone, тож ми маємо вивести обидва
#[derive(Debug, Clone, Copy)]
struct Unit;

// Кортежна структура з ресурсами, яка реалізує трейт `Clone`
// Вона НЕ МОЖЕ бути Copy, тому що Box<T> не є Copy
#[derive(Clone, Debug)]
struct Pair(Box<i32>, Box<i32>);

fn main() {
    // Створити екземпляр `Unit`
    let unit = Unit;
    // Скопіювати `Unit` — це неявне копіювання, а не переміщення!
    // Оскільки Unit реалізує Copy, значення автоматично дублюється
    let copied_unit = unit;

    // Обидва `Unit` можна використовувати незалежно
    println!("original: {:?}", unit);
    println!("copy: {:?}", copied_unit);

    // Створити екземпляр `Pair`
    let pair = Pair(Box::new(1), Box::new(2));
    println!("original: {:?}", pair);

    // Перемістити `pair` у `moved_pair`, переміщує ресурси
    // Pair не реалізує Copy, тож це переміщення
    let moved_pair = pair;
    println!("moved: {:?}", moved_pair);

    // Помилка! `pair` втратила свої ресурси
    //println!("original: {:?}", pair);
    // TODO ^ Спробуйте розкоментувати цей рядок

    // Клонувати `moved_pair` у `cloned_pair` (ресурси включено)
    // На відміну від Copy, Clone є явним — ми маємо викликати .clone()
    let cloned_pair = moved_pair.clone();
    // Вивільнити переміщену початкову пару за допомогою std::mem::drop
    drop(moved_pair);

    // Помилка! `moved_pair` було вивільнено
    //println!("moved and dropped: {:?}", moved_pair);
    // TODO ^ Спробуйте розкоментувати цей рядок

    // Результат з .clone() все ще можна використовувати!
    println!("clone: {:?}", cloned_pair);
}