Clone and Copy
Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 37 of 61.
In Rust, when you assign a value to another variable, ownership typically moves—the original variable becomes invalid. However, two standard traits change this behavior: Clone and Copy.
The Copy trait enables implicit bitwise copying. When a type implements Copy, assigning it to another variable creates an automatic copy instead of moving 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!
The Clone trait provides explicit deep copying through the .clone() method. It's required whenever you derive Copy, but can also be used alone for types that need explicit copying:
let p3 = p1.clone(); // Explicit copy
There's an important restriction: Copy can only be derived for types where all fields also implement Copy. Simple types like integers and floats are Copy, but String is not—it manages heap memory. If your struct contains a String, you can only derive Clone, not Copy.
| Trait | Behavior | Usage |
|---|---|---|
Copy | Implicit, automatic | Simple stack-only data |
Clone | Explicit via .clone() | Any duplicatable data |
For simple structs with primitive fields, deriving both traits lets you freely assign values without worrying about ownership.
Challenge
EasyLet's build a coordinate system that demonstrates the difference between Copy and Clone traits! You'll create two structs—one that can be implicitly copied and one that requires explicit cloning—to see how Rust handles duplication differently based on the traits you derive.
You'll organize your code across two files:
coordinates.rs: Define two public structs that represent different kinds of coordinates:GridPoint— with publicxandyfields (bothi32). Since it only contains primitive types, derive bothCopyandCloneso it can be implicitly copied when assigned.NamedLocation— with publicname(String) andx/yfields (i32). Since it contains aString, you can only deriveClone—notCopy. This struct will require explicit.clone()calls to duplicate.
main.rs: Bring in your coordinates module and demonstrate how each struct behaves differently when duplicated. Create instances using the inputs provided, then show that:- A
GridPointcan be assigned to another variable and both remain valid (implicit copy) - A
NamedLocationmust use.clone()to create a duplicate while keeping the original valid
- A
Your output should show both the original and copied/cloned values to prove both variables are valid after duplication:
Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})For example, with inputs 5, 10, and Home:
Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)You will receive three inputs: the x coordinate (parse as i32), the y coordinate (parse as i32), and the location name.
Cheat sheet
The Copy trait enables implicit bitwise copying. When a type implements Copy, assigning it to another variable creates an automatic copy instead of moving 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!
The Clone trait provides explicit deep copying through the .clone() method:
let p3 = p1.clone(); // Explicit copy
Copy can only be derived for types where all fields also implement Copy. Simple types like integers and floats are Copy, but String is not. If your struct contains a String, you can only derive Clone, not Copy.
| Trait | Behavior | Usage |
|---|---|---|
Copy | Implicit, automatic | Simple stack-only data |
Clone | Explicit via .clone() | Any duplicatable data |
When deriving Copy, you must also derive Clone.
Try it yourself
mod coordinates;
use coordinates::{GridPoint, NamedLocation};
fn main() {
// Read inputs
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: Create a GridPoint instance with x and y
// TODO: Demonstrate implicit copy by assigning to another variable
// (GridPoint implements Copy, so this creates a copy automatically)
// TODO: Print original and copied point
// Format: "Original point: ({x}, {y})"
// Format: "Copied point: ({x}, {y})"
// TODO: Create a NamedLocation instance with name, x, and y
// TODO: Demonstrate explicit clone (NamedLocation only implements Clone, not Copy)
// Use .clone() to create a duplicate
// TODO: Print original and cloned location
// Format: "Original location: {name} at ({x}, {y})"
// Format: "Cloned location: {name} at ({x}, {y})"
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock