Clone y Copy
Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 37 de 61.
En Rust, cuando asignas un valor a another variable, la propiedad normalmente se mueve: la variable original deja de ser válida. Sin embargo, dos traits estándar cambian este comportamiento: Clone y Copy.
El trait Copy permite la copia implícita bit a bit. 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 una copia profunda explícita mediante el método .clone(). Es obligatorio siempre que derives Copy, pero también puede usarse 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 se puede derivar para tipos cuyos campos también implementan Copy. Los tipos simples, como los enteros y los números de coma flotante, son Copy, pero String no lo es. Gestiona la memoria del montón. Si tu estructura contiene un String, solo puedes derivar Clone, no Copy.
| Trait | Comportamiento | Uso |
|---|---|---|
Copy | Implícito, automático | Datos simples solo en la pila |
Clone | Explícito mediante .clone() | Cualquier dato duplicable |
Para las estructuras simples con campos primitivos, derivar ambos rasgos te permite asignar valores libremente sin preocuparte por la propiedad.
Desafío
Fácil¡Construyamos un sistema de coordenadas que demuestre la diferencia entre los traits Copy y Clone! Crearás dos estructuras, una que se pueda copiar implícitamente y otra que requiera una clonación explícita, para ver cómo Rust gestiona la duplicación de manera diferente según los traits que derives.
Organizarás tu código en dos archivos:
coordinates.rs: Define dos estructuras públicas que representan distintos tipos de coordenadas:GridPoint: con campos públicosxey(ambosi32). Como solo contiene tipos primitivos, deriva tantoCopycomoClonepara que se pueda copiar implícitamente al asignarlo.NamedLocation: con un campo públiconame(String) y camposx/y(i32). Como contiene unString, solo puedes derivarClone, noCopy. Esta estructura requerirá llamadas explícitas a.clone()para duplicarse.
main.rs: Incorpora tu módulo de coordenadas y demuestra cómo se comporta cada estructura de manera diferente al duplicarla. Crea instancias usando las entradas proporcionadas y, después, muestra que:- Un
GridPointse puede asignar a otra variable y ambas siguen siendo válidas (copia implícita) - Un
NamedLocationdebe usar.clone()para crear un duplicado mientras el original sigue siendo 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 (analízala como i32), la coordenada y (analízala como i32) y el nombre de la ubicación.
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: Crea una instancia de GridPoint con x e y
// TODO: Demuestra la copia implícita asignando a otra variable
// (GridPoint implementa Copy, por lo que esto crea una copia automáticamente)
// TODO: Imprime el punto original y el copiado
// Format: "Original point: ({x}, {y})"
// Formato: "Copied point: ({x}, {y})"
// TODO: Crea una instancia de NamedLocation con name, x e y
// TODO: Demuestra el clone explícito (NamedLocation solo implementa Clone, no Copy)
// Usa .clone() para crear un duplicado
// TODO: Print original and cloned location
// Format: "Original location: {name} at ({x}, {y})"
// Format: "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 Programación Orientada a Objetos
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 multimediaPractica por tu cuenta: Compilador de Rust online