In this lesson, you’ll learn how to identify when the default derive(Clone) is insufficient and why you might need to manually implement the Clone trait to perform a deep copy.
The Limits of derive(Clone)
In Rust, we often use #[derive(Clone)] to duplicate data. This works perfectly for simple structs containing primitives or types that already implement Clone. However, this default implementation performs a shallow copy. It simply copies the fields as they are. If your struct owns a resource—like a Box pointing to heap memory or a Vec—a shallow copy simply copies the pointer (the address). Both the original and the clone end up pointing to the exact same heap memory.
For many types, this is fine. But if your struct represents a unique resource or a complex data structure that must be truly independent, a shallow copy creates "aliased" data that could lead to bugs or logic errors.
Manual Implementation for Deep Copying
When you need a deep copy, you must manually implement the Clone trait. A deep copy allocates new heap memory and copies the values into it, rather than just copying the pointer.
Consider this example:
struct Container {
data: Box<i32>,
}
impl Clone for Container {
fn clone(&self) -> Self {
// Deep Copy: We allocate new memory and copy the value
Container {
data: Box::new(*self.data),
}
}
}
In this case, the Clone implementation ensures that the Container clone owns its own independent heap allocation. If you had used derive(Clone), both containers would share the same Box pointer, meaning if one dropped, the other would be left with a dangling pointer or a corrupted state.
When to Go Deep?
You need to implement Clone manually when:
- Resource Ownership: Your type manages raw pointers or specific heap-allocated resources that must not be shared.
- Logical Independence: The data structure is complex, and the default "copy the fields" behavior doesn't result in a fully independent duplicate.
- Internal Accounting: You are tracking IDs or reference counts that shouldn't be duplicated verbatim during a copy.
Key Takeaway: Use #[derive(Clone)] for shallow copies, but manually implement Clone when your type owns heap resources that require their own distinct allocation to ensure true independence.