Introducción: ¿por qué Copy y Clone no son lo mismo?
\n\nCualquiera que empiece a aprender Rust, tarde o temprano se encuentra con dos traits misteriosos: Copy y Clone. A primera vista hacen lo mismo: permiten obtener un duplicado del valor. Pero el diablo, como siempre, está en los detalles.
Imagina que estás escribiendo un programa y pasas un valor a una función. En C++ sería una copia (o un movimiento, si tienes suerte). En Python, simplemente otra referencia al objeto. Y en Rust... Rust te obliga a decidir explícitamente: "Quiero que este valor sea copiado" o "Quiero clonarlo siendo consciente del costo".
\n\nAquí es exactamente donde se traza la línea entre Copy y Clone. El primero es una "copia ligera bit a bit". El segundo es una "duplicación consciente, posiblemente costosa".
Analicemos con ejemplos cuándo y qué usar, para que tu código en Rust no solo sea correcto, sino también eficiente.
\n\nBrevemente sobre lo principal: Copy vs Clone
\n\n¿Qué es Clone?
\n\nClone es un trait que te proporciona el método .clone(). Al llamarlo, le dices explícitamente al compilador: "Sé que esto puede ser costoso, pero necesito una copia independiente".
Características de Clone:
- \n
- Puede implementarse para cualquier tipo \n
- Requiere una llamada explícita a
.clone()\n - Puede realizar una copia profunda (por ejemplo, para
VecoString) \n - No cambia la semántica de propiedad: después de clonar, tienes dos valores independientes \n
¿Qué es Copy?
\n\nCopy es un trait "marcador" (sin métodos). Si un tipo implementa Copy, el compilador puede simplemente copiar sus bits al asignarlo o pasarlo a una función. Esto no requiere llamar a .clone().
Características de Copy:
- \n
- Solo funciona para tipos simples que se pueden copiar bit a bit de forma segura \n
- No requiere una llamada explícita: la copia ocurre automáticamente \n
- El tipo aún debe implementar
Clone(es un supertrait) \n - El valor original sigue siendo válido después de la copia \n
Ejemplos prácticos: cómo funciona en el código
\n\nEjemplo 1: Tipos básicos (Copy)
\n\nLos números enteros, los números de punto flotante, los valores booleanos: todos implementan Copy:
fn main() {\n let x = 42;\n let y = x; // x se copia en y, x sigue estando disponible\n \n println!("x = {}, y = {}", x, y); // ¡Funciona!\n}\n\nAquí x es un i32, que implementa Copy. Por lo tanto, al asignar y = x se produce una copia, y x sigue siendo válido.
Ejemplo 2: String (solo Clone)
\n\nEn cambio, String es un tipo complejo que almacena datos en el heap. Implementa Clone, pero no Copy:
fn main() {\n let s1 = String::from("¡Hola, Rust!");\n let s2 = s1; // s1 se mueve a s2\n \n // println!("{}", s1); // ¡Error! s1 ya no posee los datos\n \n let s3 = s2.clone(); // Así sí se puede: clonación explícita\n println!("s2 = {}, s3 = {}", s2, s3); // ¡Funciona!\n}\n\n¿Por qué String no puede ser Copy? Porque la copia bit a bit de String daría como resultado dos punteros a la misma memoria en el heap. Al hacer drop de ambos valores, se produciría una doble liberación de memoria. Clone, en cambio, crea una copia independiente completa.
Ejemplo 3: Tipo propio con Copy
\n\nPuedes hacer que tu tipo sea Copy si solo está compuesto por tipos Copy:
#[derive(Debug, Copy, Clone)]\nstruct Point {\n x: f64,\n y: f64,\n}\n\nfn main() {\n let p1 = Point { x: 1.0, y: 2.0 };\n let p2 = p1; // Se copia, p1 permanece\n \n println!