Introdução: por que Copy e Clone não são a mesma coisa?
\n\nTodo mundo que começa a estudar Rust, mais cedo ou mais tarde, se depara com duas traits misteriosas: Copy e Clone. À primeira vista, elas fazem a mesma coisa — permitem obter uma duplicata de um valor. Mas o diabo, como sempre, está nos detalhes.
Imagine que você está escrevendo um programa e passando um valor para uma função. Em C++, isso seria uma cópia (ou uma movimentação, se tivesse sorte). Em Python — apenas mais uma referência ao objeto. E em Rust... Rust obriga você a decidir explicitamente: "Eu quero que este valor seja copiado" ou "Eu quero cloná-lo com consciência dos custos".
\n\nÉ aqui que passa a fronteira entre Copy e Clone. O primeiro é uma "cópia leve, bit a bit". O segundo é uma "duplicação consciente, possivelmente cara".
Vamos entender com exemplos quando e o que usar, para que seu código em Rust seja não apenas correto, mas também eficiente.
\n\nResumindo: Copy vs Clone
\n\nO que é Clone?
\n\nClone é uma trait que fornece o método .clone(). Ao chamá-lo, você diz explicitamente ao compilador: "Eu sei que isso pode ser caro, mas preciso de uma cópia independente".
Características do Clone:
- \n
- Pode ser implementado para qualquer tipo \n
- Requer chamada explícita de
.clone()\n - Pode realizar cópia profunda (por exemplo, para
VecouString) \n - Não altera a semântica de posse — após a clonagem, você tem dois valores independentes \n
O que é Copy?
\n\nCopy é uma trait "marcadora" (sem métodos). Se um tipo implementa Copy, o compilador pode simplesmente copiar seus bits ao atribuir ou passar para uma função. Isso não requer chamar .clone().
Características do Copy:
- \n
- Funciona apenas para tipos simples que podem ser copiados com segurança bit a bit \n
- Não requer chamada explícita — a cópia ocorre automaticamente \n
- O tipo ainda deve implementar
Clone(é uma supertrait) \n - O valor original permanece válido após a cópia \n
Exemplos práticos: como funciona no código
\n\nExemplo 1: Tipos básicos (Copy)
\n\nInteiros, números de ponto flutuante, valores booleanos — todos implementam Copy:
fn main() {\n let x = 42;\n let y = x; // x é copiado para y, x ainda está disponível\n \n println!("x = {}, y = {}", x, y); // Funciona!\n}\n\nAqui, x é um i32, que implementa Copy. Portanto, ao atribuir y = x, ocorre uma cópia, e x permanece válido.
Exemplo 2: String (apenas Clone)
\n\nJá String é um tipo complexo que armazena dados no heap. Ele implementa Clone, mas não Copy:
fn main() {\n let s1 = String::from("Olá, Rust!");\n let s2 = s1; // s1 é movido para s2\n \n // println!("{}", s1); // Erro! s1 não possui mais os dados\n \n let s3 = s2.clone(); // Assim funciona — clonagem explícita\n println!("s2 = {}, s3 = {}", s2, s3); // Funciona!\n}\n\nPor que String não pode ser Copy? Porque copiar bit a bit uma String resultaria em dois ponteiros para a mesma memória no heap. Ao fazer drop de ambos os valores — liberação dupla de memória. Já Clone cria uma cópia independente completa.
Exemplo 3: Tipo próprio com Copy
\n\nVocê pode tornar seu tipo Copy se ele for composto apenas por tipos Copy:
#[derive(Debug, Copy, Clone)]\nstruct Ponto {\n x: f64,\n y: f64,\n}\n\nfn main() {\n let p1 = Ponto { x: 1.0, y: 2.0 };\n let p2 = p1; // É copiado, p1 permanece\n \n println!