Copy y Clone en Rust: ¿cuál es la diferencia y qué elegir?

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué Copy y Clone no son lo mismo?

\n\n

Cualquiera 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.

\n\n

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\n

Aquí 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".

\n\n

Analicemos con ejemplos cuándo y qué usar, para que tu código en Rust no solo sea correcto, sino también eficiente.

\n\n

Brevemente sobre lo principal: Copy vs Clone

\n\n

¿Qué es Clone?

\n\n

Clone 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".

\n\n

Características de Clone:

\n
    \n
  • Puede implementarse para cualquier tipo
  • \n
  • Requiere una llamada explícita a .clone()
  • \n
  • Puede realizar una copia profunda (por ejemplo, para Vec o String)
  • \n
  • No cambia la semántica de propiedad: después de clonar, tienes dos valores independientes
  • \n
\n\n

¿Qué es Copy?

\n\n

Copy 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().

\n\n

Características de Copy:

\n
    \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
\n\n

Ejemplos prácticos: cómo funciona en el código

\n\n

Ejemplo 1: Tipos básicos (Copy)

\n\n

Los números enteros, los números de punto flotante, los valores booleanos: todos implementan Copy:

\n\n
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\n

Aquí x es un i32, que implementa Copy. Por lo tanto, al asignar y = x se produce una copia, y x sigue siendo válido.

\n\n

Ejemplo 2: String (solo Clone)

\n\n

En cambio, String es un tipo complejo que almacena datos en el heap. Implementa Clone, pero no Copy:

\n\n
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.

\n\n

Ejemplo 3: Tipo propio con Copy

\n\n

Puedes hacer que tu tipo sea Copy si solo está compuesto por tipos Copy:

\n\n
#[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!                

Blogs

Book Recommendations