Clone e Copy
Parte da seção Object Oriented Programming do Journey de Rust da Coddy — lição 37 de 61.
Em Rust, quando você atribui um valor a outra variável, a posse (ownership) normalmente é movida — a variável original torna-se inválida. No entanto, duas traits padrão alteram esse comportamento: Clone e Copy.
O trait Copy permite a cópia bit a bit implícita. Quando um tipo implementa Copy, atribuí-lo a outra variável cria uma cópia automática em vez de mover a posse (ownership):
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1 é copiado, não movido
println!("{}, {}", p1.x, p2.x); // Ambos são válidos!
A trait Clone fornece cópia profunda explícita através do método .clone(). É necessário sempre que você deriva Copy, mas também pode ser usado sozinho para tipos que precisam de cópia explícita:
let p3 = p1.clone(); // Cópia explícita
Há uma restrição importante: Copy só pode ser derivado para tipos onde todos os campos também implementam Copy. Tipos simples como inteiros e floats são Copy, mas String não é — ele gerencia memória heap. Se sua struct contém uma String, você só pode derivar Clone, não Copy.
| Trait | Comportamento | Uso |
|---|---|---|
Copy | Implícito, automático | Dados simples apenas na stack |
Clone | Explícito via .clone() | Qualquer dado duplicável |
Para structs simples com campos primitivos, derivar ambos os traits permite que você atribua valores livremente sem se preocupar com ownership.
Desafio
FácilVamos construir um sistema de coordenadas que demonstra a diferença entre as traits Copy e Clone! Você criará duas structs—uma que pode ser copiada implicitamente e outra que requer clonagem explícita—para ver como o Rust lida com a duplicação de forma diferente com base nas traits que você deriva.
Você organizará seu código em dois arquivos:
coordinates.rs: Defina duas structs públicas que representam diferentes tipos de coordenadas:GridPoint— com campos públicosxey(ambosi32). Como ele contém apenas tipos primitivos, derive tantoCopyquantoClonepara que ele possa ser copiado implicitamente quando atribuído.NamedLocation— com campos públicosname(String) ex/y(i32). Como ele contém umaString, você só pode derivarClone—nãoCopy. Esta struct exigirá chamadas explícitas a.clone()para ser duplicada.
main.rs: Importe seu módulo de coordenadas e demonstre como cada struct se comporta de forma diferente ao ser duplicada. Crie instâncias usando as entradas fornecidas e, em seguida, mostre que:- Um
GridPointpode ser atribuído a outra variável e ambos permanecem válidos (cópia implícita) - Um
NamedLocationdeve usar.clone()para criar uma duplicata enquanto mantém o original válido
- Um
Sua saída deve mostrar tanto os valores originais quanto os copiados/clonados para provar que ambas as variáveis são válidas após a duplicação:
Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})Por exemplo, com as entradas 5, 10, e Home:
Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)Você receberá três entradas: a coordenada x (converta para i32), a coordenada y (converta para i32) e o nome do local.
Folha de consulta
A trait Copy permite a cópia bitwise implícita. Quando um tipo implementa Copy, atribuí-lo a outra variável cria uma cópia automática em vez de mover a posse (ownership):
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1 is copied, not moved
println!("{}, {}", p1.x, p2.x); // Both are valid!
A trait Clone fornece cópia profunda (deep copy) explícita através do método .clone():
let p3 = p1.clone(); // Explicit copy
Copy só pode ser derivado para tipos onde todos os campos também implementam Copy. Tipos simples como inteiros e floats são Copy, mas String não é. Se sua struct contém uma String, você só pode derivar Clone, não Copy.
| Trait | Comportamento | Uso |
|---|---|---|
Copy | Implícito, automático | Dados simples apenas na stack |
Clone | Explícito via .clone() | Qualquer dado duplicável |
Ao derivar Copy, você também deve derivar Clone.
Experimente você mesmo
mod coordinates;
use coordinates::{GridPoint, NamedLocation};
fn main() {
// Ler 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: Criar uma instância de GridPoint com x e y
// TODO: Demonstrar cópia implícita atribuindo a outra variável
// (GridPoint implementa Copy, então isso cria uma cópia automaticamente)
// TODO: Imprimir o ponto original e o copiado
// Format: "Original point: ({x}, {y})"
// Format: "Copied point: ({x}, {y})"
// TODO: Criar uma instância de NamedLocation com name, x, e y
// TODO: Demonstrar clone explícito (NamedLocation apenas implementa Clone, não Copy)
// Usar .clone() para criar uma duplicata
// TODO: Imprimir a localização original e a clonada
// Format: "Original location: {name} at ({x}, {y})"
// Format: "Cloned location: {name} at ({x}, {y})"
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Métodos e Comportamento
Introdução a Blocos de ImplementaçãoO Parâmetro SelfMétodos MutáveisFunções AssociadasMúltiplos Blocos de ImplementaçãoEncadeamento de MétodosRecapitulação - Ações de Retângulo4Projeto: Pet Virtual
Definindo o PetAlimentando o Pet7Traits Padrão
A Trait DebugA Trait DisplayClone e CopyTraits de IgualdadeRecapitulação - Ponto Imprimível10Projeto: Sistema de Documentos
A Trait DrawComponente de Texto2Encapsulamento e Módulos
Fundamentos de MódulosA Palavra-chave PublicCampos PrivadosGettersSettersRecapitulação - Cofre Seguro5Genéricos
Structs GenéricasMétodos GenéricosMúltiplos Tipos GenéricosFunções GenéricasRecapitulação - Ponto de Coordenada8Traits como Restrições
Sintaxe de Restrições de TraitRestrições MúltiplasA Cláusula WhereRetornando Tipos com TraitsRecapitulação - Impressora Genérica11Padrões de Projeto em Rust
Padrão NewtypeComposiçãoA Trait DropFrom e IntoRecapitulação - Mock de Smart Pointer3Enums Avançados
Enums com DadosMétodos em EnumsCorrespondência de Variantes de DadosO Enum Option RevisitadoRecapitulação - Enum Shape6Definição de Traits
O que é uma Trait?Implementando TraitsImplementações PadrãoSobrescrevendo PadrõesTraits com ParâmetrosRecapitulação - Media Player