C developers often read assignment and parameter passing as copying a value:
uint32_t a = 10;
uint32_t b = a;
Struct values can also be copied:
struct Point p1 = {1, 2};
struct Point p2 = p1;
But once a struct contains a pointer, the meaning changes:
struct Buffer {
uint8_t *data;
size_t len;
};
struct Buffer b2 = b1; // only copies pointer and length
That is not a deep copy. b1.data and b2.data point to the same allocation. Who frees it? Can both mutate it? Can one still use it after the other frees it? C handles this by convention.
Rust splits this into three concepts:
move -> ownership transfers; the old value cannot be used
Copy -> simple implicit copy; the old value remains usable
Clone -> explicit copy; may allocate or be expensive
Simple Values Are Usually Copy
Integers, booleans, and floats copy on assignment and by value:
let a: u32 = 10;
let b = a;
println!("{a}");
println!("{b}");
u32 implements Copy. b gets its own value, and a remains usable.
Function parameters work the same way:
fn print_id(id: u32) {
println!("{id}");
}
let id = 42_u32;
print_id(id);
println!("{id}");
This is close to passing integers by value in C.
If all fields of a struct are Copy, the struct can be Copy too:
#[derive(Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("{}", p1.x);
A Copy type should be cheap, direct, and free of resource-ownership ambiguity.
Resource-Owning Values Move by Default
String, Vec<T>, and File own resources. Assignment moves ownership instead of copying the resource:
let a = String::from("device");
let b = a;
// println!("{a}"); // not allowed
println!("{b}");
This is closer to transferring ownership of a pointer-owning C struct than to deep copying it. Rust invalidates the old variable so two values do not both think they own the same resource.
Vec<u8> is the same:
let packet = vec![1_u8, 2, 3];
let moved = packet;
// packet cannot be used
println!("{}", moved.len());
Passing to a function also moves:
fn consume(data: Vec<u8>) {
println!("{}", data.len());
}
let data = vec![1_u8, 2, 3];
consume(data);
// data cannot be used
If the function only reads, borrow:
fn inspect(data: &[u8]) {
println!("{}", data.len());
}
let data = vec![1_u8, 2, 3];
inspect(&data);
println!("{}", data.len());
Clone Is Explicit Copying
If you really need two owned values, use clone():
let a = String::from("device");
let b = a.clone();
println!("{a}");
println!("{b}");
For String, clone() allocates new memory and copies text. For Vec<T>, it copies elements.
let a = vec![1_u8, 2, 3];
let b = a.clone();
This is closer to a C deep copy:
uint8_t *copy = malloc(len);
memcpy(copy, src, len);
Do not use clone() as the default way to silence ownership errors. It is a real operation and may allocate.
How Copy and Clone Relate
Copy is a stronger promise: copying may happen implicitly, and it should behave like copying a simple value.
Clone is explicit:
let b = a.clone();
Every Copy type should also be Clone, so this derive is common:
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct DeviceId(u32);
This newtype only wraps a u32, so it has no resource-ownership ambiguity and is a good Copy candidate.
This type should not be Copy:
struct Packet {
data: Vec<u8>,
}
If copying Packet only copied the Vec pointer, length, and capacity, two values would point to the same heap allocation. Rust does not allow Vec<u8> to be Copy, so this Packet cannot be automatically Copy.
If you want to copy the packet, make it explicit:
#[derive(Clone)]
struct Packet {
data: Vec<u8>,
}
let p2 = p1.clone();
Clone Bounds in Function Signatures
Generic code must ask for Clone if it needs to clone a T:
fn duplicate<T: Clone>(value: T) -> (T, T) {
(value.clone(), value)
}
If only simple copyable values are allowed, ask for Copy:
fn duplicate_copy<T: Copy>(value: T) -> (T, T) {
(value, value)
}
The bounds mean different things:
T: Copy -> implicit copying is allowed, suitable for simple values
T: Clone -> explicit copying is available, possibly with cost
Do not add Clone to generic APIs just for convenience. It becomes a requirement on the caller’s type.
Migrating C Struct Pass-by-Value
C commonly passes small structs by value:
struct Point {
int x;
int y;
};
void draw_point(struct Point p);
Rust can make that type Copy:
#[derive(Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn draw_point(p: Point) {
println!("{}, {}", p.x, p.y);
}
But if the struct owns heap memory:
struct Config {
name: String,
}
Passing by value means taking ownership:
fn set_config(config: Config) {
// takes config
}
If the function only reads it, take a borrow:
fn print_config(config: &Config) {
println!("{}", config.name);
}
This is one of the core API differences: pass-by-value is not always a cheap copy. It may mean ownership transfer.
What to Keep
The first C-to-Rust mapping:
integer / small POD by value -> Copy
assignment of malloc-owning struct -> move by default, avoids double free
manual deep copy -> Clone / clone()
function only reads -> borrow; do not clone
generic code needs copying -> T: Clone or T: Copy
Vec/String/File resources -> move by default, not Copy
move, Copy, and Clone answer one question: did this assignment or parameter passing transfer ownership, cheaply copy a simple value, or explicitly duplicate a resource?