Skip to content

Move vs Copy

JavaScript’s blurry line between value and reference

Section titled “JavaScript’s blurry line between value and reference”

In JavaScript, primitives (number, boolean, string) are passed by value — copying them is cheap and automatic. Objects and arrays are passed by reference — both variables point to the same heap data.

TypeScript exposes this through the type system but does not give you control over it. You cannot make your own type behave like a primitive.

Rust is explicit: types either implement the Copy trait (and are automatically duplicated on assignment) or they follow move semantics (ownership transfers). You can also derive Clone for an explicit, deep copy.

Copy types — stack values that duplicate automatically

Section titled “Copy types — stack values that duplicate automatically”

Copy types are stack-allocated and cheap to duplicate. Assigning or passing them creates a full independent copy with no extra syntax needed.

TypeScript
// TypeScript — primitives copy automatically (same as Rust Copy)
let a = 42;
let b = a; // b is an independent copy
b = 100;
console.log(a); // 42 — a is unchanged
// But TypeScript has no way to make a struct behave like this:
const p1 = { x: 1, y: 2 };
const p2 = p1; // p2 is a reference — they share the same object
p2.x = 99;
console.log(p1.x); // 99 — p1 was mutated through p2
Rust
fn main() {
// i32, f64, bool, char are all Copy
let a: i32 = 42;
let b = a; // copied — a is still valid
println!("a={} b={}", a, b);
// Tuples of Copy types are also Copy
let t1 = (1, 2.0, true);
let t2 = t1;
println!("t1={:?} t2={:?}", t1, t2);
}

Move types — heap values that transfer ownership

Section titled “Move types — heap values that transfer ownership”

Types like String and Vec<T> own heap-allocated data. Assigning them moves ownership. If you need two independent copies, you call .clone() explicitly.

TypeScript
// TypeScript — strings are primitives, always copied
let s1 = "hello";
let s2 = s1;
console.log(s1, s2); // both valid — strings are primitives in JS
// Objects require spread for a shallow copy
const obj1 = { items: [1, 2, 3] };
const obj2 = { ...obj1 }; // shallow clone
const obj3 = structuredClone(obj1); // deep clone (ES2022)
console.log(obj1.items, obj2.items);
Rust
#[derive(Debug, Clone)]
struct Point {
x: f64,
y: f64,
}
fn main() {
// String — heap value, uses move semantics
let s1 = String::from("hello");
let s2 = s1.clone(); // explicit deep copy
println!("s1={} s2={}", s1, s2); // both valid
// Your own struct — derive Clone to get .clone()
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone();
println!("p1={:?} p2={:?}", p1, p2);
// Without Clone, assignment moves ownership:
let p3 = Point { x: 3.0, y: 4.0 };
let p4 = p3; // p3 is moved — p3 is no longer valid
println!("p4={:?}", p4);
}
#[derive(Debug, Clone)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let a: i32 = 42;
let b = a;
println!("a={} b={} (both valid, i32 is Copy)", a, b);
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone();
println!("p1={:?} p2={:?}", p1, p2);
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1={} s2={}", s1, s2);
}
Which of the following types implements the `Copy` trait?
What is the idiomatic way to create an independent copy of a `String` in Rust?
Why doesn't `String` implement `Copy`?