Move vs Copy
เส้นแบ่งที่เลือนลางของ JavaScript ระหว่าง value และ reference
หัวข้อที่มีชื่อว่า “เส้นแบ่งที่เลือนลางของ JavaScript ระหว่าง value และ reference”ใน JavaScript, primitive (number, boolean, string) ถูกส่ง by value — การ copy ราคาถูกและอัตโนมัติ Object และ array ถูกส่ง by reference — ทั้งสองตัวแปรชี้ไปที่ heap data เดียวกัน
TypeScript สะท้อนเรื่องนี้ผ่าน type system แต่ไม่เปิดให้คุณควบคุม คุณทำให้ type ของตัวเองทำงานเหมือน primitive ไม่ได้
Rust ชัดเจน: type ไม่ implement Copy trait (และถูก duplicate อัตโนมัติเมื่อ assign) ก็จะ follow move semantics (ownership โอน) คุณสามารถ derive Clone สำหรับ explicit deep copy ได้ด้วย
Copy type — stack value ที่ duplicate อัตโนมัติ
หัวข้อที่มีชื่อว่า “Copy type — stack value ที่ duplicate อัตโนมัติ”Copy type อยู่บน stack และ duplicate ได้ถูกมาก การ assign หรือส่งเข้า function จะได้ copy ที่เป็นอิสระเต็มตัว โดยไม่ต้องเขียน syntax พิเศษ
// TypeScript — primitive copy อัตโนมัติ (เหมือน Rust Copy)let a = 42;let b = a; // b เป็น copy อิสระb = 100;console.log(a); // 42 — a ไม่เปลี่ยน
// แต่ TypeScript ไม่มีวิธีให้ struct ทำงานแบบนี้:const p1 = { x: 1, y: 2 };const p2 = p1; // p2 เป็น reference — share object เดียวกันp2.x = 99;console.log(p1.x); // 99 — p1 ถูก mutate ผ่าน p2fn main() { // i32, f64, bool, char ล้วนเป็น Copy let a: i32 = 42; let b = a; // copied — a ยังใช้ได้ println!("a={} b={}", a, b);
// Tuple ของ Copy type ก็เป็น Copy let t1 = (1, 2.0, true); let t2 = t1; println!("t1={:?} t2={:?}", t1, t2);}Move type — heap value ที่โอน ownership
หัวข้อที่มีชื่อว่า “Move type — heap value ที่โอน ownership”Type อย่าง String และ Vec<T> เป็น owner ของ heap-allocated data การ assign จึงเป็นการ move ownership ถ้าอยากได้ copy อิสระสองชุด ต้องเรียก .clone() เอง
// TypeScript — string เป็น primitive copy อัตโนมัติlet s1 = "hello";let s2 = s1;console.log(s1, s2); // ทั้งคู่ใช้ได้ — string เป็น primitive ใน JS
// Object ต้องใช้ spread สำหรับ shallow copyconst obj1 = { items: [1, 2, 3] };const obj2 = { ...obj1 }; // shallow cloneconst obj3 = structuredClone(obj1); // deep clone (ES2022)console.log(obj1.items, obj2.items);#[derive(Debug, Clone)]struct Point { x: f64, y: f64,}
fn main() { // String — heap value ใช้ move semantics let s1 = String::from("hello"); let s2 = s1.clone(); // explicit deep copy println!("s1={} s2={}", s1, s2); // ทั้งคู่ใช้ได้
// struct ของคุณ — derive Clone เพื่อใช้ .clone() let p1 = Point { x: 1.0, y: 2.0 }; let p2 = p1.clone(); println!("p1={:?} p2={:?}", p1, p2);
// ถ้าไม่มี Clone การ assign จะ move ownership: let p3 = Point { x: 3.0, y: 4.0 }; let p4 = p3; // p3 ถูก move — p3 ใช้ไม่ได้แล้ว 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);}Compiling…