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() | Любые дублируемые данные |
Для простых структур с примитивными полями автоматический вывод обоих типажей позволяет свободно присваивать значения, не беспокоясь о владении.
Задание
ЛегкоДавайте создадим систему координат, которая демонстрирует разницу между трейтами 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})"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player