Clone y Copy
Parte de la sección Object Oriented Programming del Journey de Rust de Coddy — lección 37 de 61.
En Rust, cuando asignas un valor a otra variable, la propiedad (ownership) generalmente se mueve: la variable original se vuelve inválida. Sin embargo, dos traits estándar cambian este comportamiento: Clone y Copy.
El trait Copy permite el copiado bit a bit implícito. Cuando un tipo implementa Copy, asignarlo a otra variable crea una copia automática en lugar de mover la propiedad:
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1 se copia, no se mueve
println!("{}, {}", p1.x, p2.x); // ¡Ambos son válidos!
El trait Clone proporciona copia profunda explícita a través del método .clone(). Es necesario siempre que derives Copy, pero también se puede usar por sí solo para tipos que necesitan una copia explícita:
let p3 = p1.clone(); // Copia explícita
Hay una restricción importante: Copy solo puede derivarse para tipos en los que todos los campos también implementen Copy. Los tipos simples como los enteros y los flotantes son Copy, pero String no lo es; este gestiona memoria en el montón (heap). Si tu struct contiene un String, solo puedes derivar Clone, no Copy.
| Rasgo | Comportamiento | Uso |
|---|---|---|
Copy | Implícito, automático | Datos simples solo en pila |
Clone | Explícito mediante .clone() | Cualquier dato duplicable |
Para structs simples con campos primitivos, derivar ambos traits te permite asignar valores libremente sin preocuparte por la propiedad (ownership).
Desafío
Fácil¡Vamos a construir un sistema de coordenadas que demuestre la diferencia entre los traits Copy y Clone! Crearás dos structs—uno que se puede copiar implícitamente y otro que requiere una clonación explícita—para ver cómo Rust maneja la duplicación de manera diferente según los traits que derives.
Organizarás tu código en dos archivos:
coordinates.rs: Define dos structs públicos que representen diferentes tipos de coordenadas:GridPoint— con campos públicosxey(ambosi32). Dado que solo contiene tipos primitivos, deriva tantoCopycomoClonepara que pueda copiarse implícitamente al ser asignado.NamedLocation— con campos públicosname(String) yx/y(i32). Dado que contiene unString, solo puedes derivarClone—noCopy. Este struct requerirá llamadas explícitas a.clone()para duplicarse.
main.rs: Importa tu módulo de coordenadas y demuestra cómo cada struct se comporta de manera diferente al ser duplicado. Crea instancias utilizando las entradas proporcionadas, luego muestra que:- Un
GridPointpuede asignarse a otra variable y ambos permanecen válidos (copia implícita) - Un
NamedLocationdebe usar.clone()para crear un duplicado manteniendo el original válido
- Un
Tu salida debe mostrar tanto los valores originales como los copiados/clonados para demostrar que ambas variables son válidas después de la duplicación:
Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})Por ejemplo, con las entradas 5, 10, y Home:
Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)Recibirás tres entradas: la coordenada x (analizada como i32), la coordenada y (analizada como i32), y el nombre de la ubicación.
Hoja de referencia
El trait Copy permite el copiado bit a bit implícito. Cuando un tipo implementa Copy, asignarlo a otra variable crea una copia automática en lugar de mover la propiedad:
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1 se copia, no se mueve
println!("{}, {}", p1.x, p2.x); // ¡Ambos son válidos!
El trait Clone proporciona copiado profundo explícito a través del método .clone():
let p3 = p1.clone(); // Copia explícita
Copy solo se puede derivar para tipos donde todos los campos también implementan Copy. Los tipos simples como enteros y flotantes son Copy, pero String no lo es. Si tu struct contiene un String, solo puedes derivar Clone, no Copy.
| Trait | Comportamiento | Uso |
|---|---|---|
Copy | Implícito, automático | Datos simples solo en pila (stack) |
Clone | Explícito mediante .clone() | Cualquier dato duplicable |
Al derivar Copy, también debes derivar Clone.
Pruébalo tú mismo
mod coordinates;
use coordinates::{GridPoint, NamedLocation};
fn main() {
// Leer entradas
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: Crear una instancia de GridPoint con x e y
// TODO: Demostrar la copia implícita asignando a otra variable
// (GridPoint implementa Copy, por lo que esto crea una copia automáticamente)
// TODO: Imprimir el punto original y el copiado
// Formato: "Original point: ({x}, {y})"
// Formato: "Copied point: ({x}, {y})"
// TODO: Crear una instancia de NamedLocation con name, x e y
// TODO: Demostrar la clonación explícita (NamedLocation solo implementa Clone, no Copy)
// Usar .clone() para crear un duplicado
// TODO: Imprimir la ubicación original y la clonada
// Formato: "Original location: {name} at ({x}, {y})"
// Formato: "Cloned location: {name} at ({x}, {y})"
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Métodos y Comportamiento
Intro a Bloques de ImplementaciónEl Parámetro SelfMétodos MutablesFunciones AsociadasMúltiples Bloques de ImplementaciónEncadenamiento de MétodosResumen - Acciones de Rectángulo4Proyecto: Mascota virtual
Definir la mascotaAlimentar a la mascota7Traits estándar
El trait DebugEl trait DisplayClone y CopyTraits de igualdadResumen - Punto imprimible10Proyecto: Sistema de documentos
El Trait DrawComponente de texto2Encapsulamiento y módulos
Conceptos básicos de módulosLa palabra clave PublicCampos privadosGettersSettersRepaso - Casillero seguro5Genéricos
Estructuras genéricasMétodos genéricosMúltiples tipos genéricosFunciones genéricasResumen - Punto de coordenadas8Traits como límites
Sintaxis de límites de traitsLímites múltiplesLa cláusula whereRetornar tipos con traitsResumen: Impresora genérica11Patrones de diseño en Rust
Patrón NewtypeComposiciónEl Trait DropFrom e IntoResumen - Mock de Smart Pointer3Enums avanzados
Enums con datosMétodos en EnumsCoincidencia de variantes de datosEl Enum Option revisitadoResumen - Enum Shape6Definición de Traits
¿Qué es un Trait?Implementación de TraitsImplementaciones por defectoSobrescribir valores por defectoTraits con parámetrosResumen - Reproductor multimedia