WIP: Copy vs Move in Rust
WIP!!!!
Now I’m a Rust beginner.
This is a personal note about the difference between Copy(コピー) and Move(ムーブ).
The conclusion first
In Rust, values are handled in one of two ways:
- Copy: the value is duplicated
- Move: ownership of the value is transferred
Which one happens is determined at compile time, by the type(型).
A Copy example
let a = 10;
let b = a;
println!("{}", a); // OK
Primitive numeric types such as i32 implement the Copy trait.
Notes
A trait(トレイト) represents a specification of behavior. Because Rust does not support class inheritance(継承), traits are the primary way to define and share behavior among types.
Characteristics:
- Fixed size
- Stored on the stack
- Safe to copy bit-by-bit
Because of this, assigning a to b does not invalidate a.
A Move example (the first pitfall)
let s = String::from("hello");
let t = s;
println!("{}", s); // compile error
At this point, s can no longer be used.
Reason:
Stringdoes not implementCopy- Assignment transfers ownership from
stot
Official reference:
Why String is not Copy
Internally, a String consists of:
- A pointer to heap-allocated data
- Length
- Capacity
If Rust allowed a shallow copy:
- Multiple variables would point to the same heap memory
- Deallocation timing would become ambiguous
- Double-free bugs would be possible
Rust avoids this not with a garbage collector, but by preventing the situation at the language design level.
Official reference:
How Copy vs Move is decided
The rule is simple:
- If a type implements
Copy→ Copy - Otherwise → Move
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
This struct is Copy because all of its fields are Copy.
However, if any field is a String or other non-Copy type,
the struct itself cannot be Copy.
Official reference:
What clone() actually does
let s = String::from("hi");
let t = s.clone();
clone() performs an explicit deep copy:
- Heap allocation included
- Cost is not hidden
- Intent is visible in the code
This is why Rust makes clone() explicit and noisy.
Official reference:
Personal rule:
- Only call
clone()when I can explain why it is necessary
Copy and Move in function arguments
fn takes_string(s: String) {}
fn takes_ref(s: &String) {}
- Passing by value → Move
- Passing by reference → no Move
Function calls are not ownership-safe boundaries. Arguments follow the same ownership rules as assignments.
Official reference:
A common compiler error
value borrowed here after move
This means:
- The value was moved
- And then used again afterward
Rust’s compiler errors are unusually descriptive. Treat them as part of the language documentation.
Notes to my future self
- Copy is the exception, Move is the default
- Always question ownership when seeing
= clone()is an escape hatch, not a habit- Compiler errors are guidance, not obstacles
Rust is strict, but that strictness exists to make invalid states unrepresentable.