Menu
Coddy logo textTech

Clone и Copy

Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 37 из 61.

В Rust, когда вы присваиваете значение другой переменной, владение обычно перемещается — исходная переменная становится недействительной. Однако два стандартных типажа изменяют это поведение: Clone и Copy.

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

#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 10, y: 20 };
let p2 = p1;  // p1 копируется, а не перемещается

println!("{}, {}", p1.x, p2.x);  // Оба валидны!

Трейт Clone обеспечивает явное глубокое копирование с помощью метода .clone(). Он необходим всякий раз, когда вы используете derive для Copy, но также может использоваться отдельно для типов, которым требуется явное копирование:

let p3 = p1.clone();  // Явное копирование

Существует важное ограничение: Copy может быть реализован только для типов, в которых все поля также реализуют Copy. Простые типы, такие как целые числа и числа с плавающей запятой, реализуют Copy, но String — нет, так как он управляет памятью в куче. Если ваша структура содержит String, вы можете реализовать только Clone, а не Copy.

ТрейтПоведениеИспользование
CopyНеявное, автоматическоеПростые данные, хранящиеся только в стеке
CloneЯвное через .clone()Любые дублируемые данные

Для простых структур с примитивными полями автоматический вывод обоих типажей позволяет свободно присваивать значения, не беспокоясь о владении.

challenge icon

Задание

Легко

Давайте создадим систему координат, которая демонстрирует разницу между трейтами Copy и Clone! Вы создадите две структуры — одну, которую можно копировать неявно, и другую, требующую явного клонирования, — чтобы увидеть, как Rust по-разному обрабатывает дублирование в зависимости от производных трейтов.

Вы организуете свой код в двух файлах:

  • coordinates.rs: Определите две публичные структуры, представляющие различные виды координат:
    • GridPoint — с публичными полями x и y (оба i32). Поскольку она содержит только примитивные типы, выведите (derive) оба трейта Copy и Clone, чтобы её можно было неявно копировать при присваивании.
    • NamedLocation — с публичными полями name (String) и x/y (i32). Поскольку она содержит String, вы можете вывести только Clone, но не Copy. Эта структура потребует явных вызовов .clone() для дублирования.
  • main.rs: Подключите модуль coordinates и продемонстрируйте, как каждая структура ведет себя по-разному при дублировании. Создайте экземпляры, используя предоставленные входные данные, а затем покажите, что:
    • GridPoint можно присвоить другой переменной, и обе останутся валидными (неявное копирование)
    • NamedLocation должна использовать .clone() для создания дубликата, сохраняя оригинал валидным

Ваш вывод должен показывать как оригинальные, так и скопированные/клонированные значения, чтобы доказать, что обе переменные валидны после дублирования:

Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})

Например, с входными данными 5, 10 и Home:

Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)

Вы получите три входных значения: координату x (распарсить как i32), координату y (распарсить как i32) и название местоположения.

Шпаргалка

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

#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 10, y: 20 };
let p2 = p1;  // p1 копируется, а не перемещается

println!("{}, {}", p1.x, p2.x);  // Оба валидны!

Трейт Clone обеспечивает явное глубокое копирование через метод .clone():

let p3 = p1.clone();  // Явное копирование

Copy может быть автоматически реализован (derived) только для типов, в которых все поля также реализуют Copy. Простые типы, такие как целые числа и числа с плавающей запятой, реализуют Copy, но String — нет. Если ваша структура содержит String, вы можете реализовать только Clone, но не Copy.

ТрейтПоведениеИспользование
CopyНеявное, автоматическоеПростые данные, хранящиеся только в стеке
CloneЯвное через .clone()Любые дублируемые данные

При автоматической реализации Copy вы также должны реализовать Clone.

Попробуйте сами

mod coordinates;

use coordinates::{GridPoint, NamedLocation};

fn main() {
    // Чтение входных данных
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let x: i32 = input1.trim().parse().expect("Invalid number");

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let y: i32 = input2.trim().parse().expect("Invalid number");

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let name = input3.trim().to_string();

    // TODO: Создать экземпляр GridPoint с x и y

    // TODO: Продемонстрировать неявное копирование путем присваивания другой переменной
    // (GridPoint реализует Copy, поэтому копия создается автоматически)

    // TODO: Вывести исходную и скопированную точки
    // Формат: "Original point: ({x}, {y})"
    // Формат: "Copied point: ({x}, {y})"

    // TODO: Создать экземпляр NamedLocation с name, x и y

    // TODO: Продемонстрировать явное клонирование (NamedLocation реализует только Clone, а не Copy)
    // Использовать .clone() для создания дубликата

    // TODO: Вывести исходное и клонированное местоположение
    // Формат: "Original location: {name} at ({x}, {y})"
    // Формат: "Cloned location: {name} at ({x}, {y})"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming