การดู : 178

27/04/2026 03:20น.

ตัวอย่างการใช้ Reference และ Borrow Checker

การใช้งาน Borrowing และ References (การยืมข้อมูล) | Rust The Series EP.8

#สอนภาษา Rust

#Rust Borrowing

#References

#Rust

ยินดีต้อนรับเพื่อนๆ ชาว Dev เข้าสู่ Rust The Series ครับ! ใน EP.7 เราได้เห็นความเฮี้ยน (แต่ปลอดภัย) ของกฎ Ownership ไปแล้ว ที่พอย้ายค่า (Move) ปุ๊บ ตัวแปรเดิมก็ตายทันที จนหลายคนอาจจะเริ่มท้อแล้วสงสัยว่า:

"โหยพี่... ถ้าผมแค่จะส่งค่าไปให้ฟังก์ชันอ่านเฉยๆ ผมต้องมานั่ง .clone() ให้เสีย Memory หรือต้องคอย return ค่ากลับมาคืนเจ้าของเดิมทุกรอบเลยเหรอ?"

คำตอบคือ "ไม่ต้องครับ!" เพราะ Rust มีระบบที่เรียกว่า Borrowing (การยืม) ผ่านสิ่งที่เรียกว่า References (&) ที่จะช่วยให้ชีวิตเราง่ายขึ้นเยอะ

1. การยืมแบบอ่านอย่างเดียว (Immutable References)

การยืมใน Rust เปรียบได้กับการยืมหนังสือจากห้องสมุดครับ:

  • คุณเอาไปอ่านได้ (Access data)

  • แต่คุณไม่ใช่เจ้าของ (No Ownership)

  • และที่สำคัญคือ คุณห้ามขีดเขียนหรือแก้ไขเนื้อหาในหนังสือเล่มนั้นเด็ดขาด!

ในภาษา Rust เราจะใช้เครื่องหมาย & นำหน้าชื่อตัวแปร เพื่อบอกคอมไพเลอร์ว่า "นี่คือการส่งแบบอ้างอิง (Reference) นะ ผมแค่ให้ยืมไปดู ไม่ได้ยกสิทธิ์เจ้าของให้"

Rust

fn main() {
    let s1 = String::from("Superdev");

    // ส่งแบบ &s1 คือการ "ให้ยืมอ่าน"
    let len = calculate_length(&s1); 

    // ✅ s1 ยังใช้งานได้ปกติ! เพราะสิทธิ์ความเป็นเจ้าของยังอยู่ที่นี่
    println!("ความยาวของ '{}' คือ {}", s1, len); 
}

fn calculate_length(s: &String) -> usize { // รับพารามิเตอร์เป็น Reference (&)
    s.len()
} // พอจบฟังก์ชัน s ซึ่งเป็น "ผู้ยืม" จะหลุด Scope ไป 
  // แต่ข้อมูลจริงใน Memory ไม่โดนลบ เพราะ s ไม่ใช่เจ้าของตัวจริง

💡 Pro Tip: สังเกตไหมครับว่าตอนเรียกใช้ calculate_length(&s1) เราต้องใส่ & และที่ตัวรับในฟังก์ชัน (s: &String) ก็ต้องใส่ & เช่นกัน เพื่อเป็นการตกลงกันทั้งคนให้และคนรับว่า "นี่คือการยืมนะ"

2. เมื่ออยากแก้ไข ต้องยืมแบบ Mutable (&mut)

ถ้าการยืมแบบปกติคือการขอยืมไปอ่าน การยืมแบบ Mutable Reference ก็คือการขอยืมไปแก้ไขครับ

เปรียบเหมือนคุณยืมหนังสือจากเพื่อนมา แล้วขออนุญาตเพื่อนว่า "นายๆ เราขอจดโน้ตเพิ่มลงไปในหนังสือหน่อยนะ" ซึ่งเพื่อนจะอนุญาตได้ ก็ต่อเมื่อตัวเพื่อนเอง (เจ้าของ) ยอมให้หนังสือนั้นถูกขีดเขียนได้ตั้งแต่แรก

ใน Rust เราจะใช้เครื่องหมาย &mut แต่มีเงื่อนไขสำคัญคือ: ตัวแปรต้นทางต้องถูกประกาศเป็น mut ไว้ตั้งแต่แรกด้วย

Rust

fn main() {
    // 1. ต้นทางต้องเป็น mut ถึงจะให้คนอื่นยืมไปแก้ได้
    let mut s = String::from("Hello");

    // 2. ตอนส่ง ต้องใส่ &mut เพื่อยืนยันว่า "ให้ยืมไปแก้"
    change(&mut s); 

    println!("{}", s); // ผลลัพธ์จะเป็น "Hello, Superdev"
}

fn change(some_string: &mut String) { // 3. ตัวรับต้องระบุว่าเป็น &mut String
    some_string.push_str(", Superdev");
}

⚠️ ข้อควรระวัง: แม้จะยืมไปแก้ได้ แต่ Rust ใจร้ายกว่าเพื่อนคุณครับ เพราะเขากำหนดว่า "ในเวลาเดียวกัน คุณจะให้คนยืมไปแก้ (&mut) ได้แค่คนเดียวเท่านั้น" (ห้ามรุมแก้พร้อมกัน) เดี๋ยวเราจะไปดูเหตุผลกันในหัวข้อถัดไปครับ

3. กฎเหล็กของการยืม (The Rules of Borrowing)

เพื่อให้เกิดความปลอดภัยสูงสุดและป้องกันฝันร้ายของโปรแกรมเมอร์ที่เรียกว่า Data Race (การที่ข้อมูลถูกแก้ไขพร้อมกันจนพัง) Rust จึงตั้งกฎเหล็กที่คอมไพเลอร์จะตรวจยิบ 2 ข้อครับ:

กฎข้อที่ 1: จะอ่านกี่คนก็ได้ แต่ถ้าจะแก้... ต้องแก้คนเดียว

ในเวลาเดียวกัน คุณสามารถถือ Reference ได้เพียง 1 ใน 2 แบบนี้เท่านั้น:

  • มี "คนยืมอ่าน" (&) กี่คนก็ได้ (ไม่จำกัดจำนวน) — เหมือนการอ่านวิกิพีเดีย ใครจะอ่านพร้อมกันก็ได้ ข้อมูลไม่เปลี่ยน

  • หรือ มี "คนยืมแก้" (&mut) ได้เพียง "คนเดียว" เท่านั้น! — และในช่วงที่คนนี้แก้ ห้ามมีคนอื่นแอบอ่านด้วยนะ

กฎข้อที่ 2: Reference ต้องไม่ตายก่อนเจ้าของ (No Dangling References)

คุณจะยืมของจากคนที่หายตัวไปแล้วไม่ได้ ตัวแปรที่ยืมไปใช้จะต้องมั่นใจว่าเจ้าของข้อมูลจริงยังมีชีวิตอยู่ในหน่วยความจำตลอดช่วงเวลาที่ถูกยืม

ลองมาดูตัวอย่างที่คอมไพเลอร์จะด่าเราอย่างแรงครับ:

Rust

fn main() {
    let mut s = String::from("hello");

    let r1 = &s;     // ✅ ยืมอ่าน คนที่ 1 (ผ่าน)
    let r2 = &s;     // ✅ ยืมอ่าน คนที่ 2 (ผ่าน)
    
    // ❌ ERROR! คอมไพเลอร์จะเบรกทันที
    let r3 = &mut s; 

    println!("{}, {}, and {}", r1, r2, r3);
}

ทำไมถึงไม่ได้?

ลองจินตนาการว่า r1 และ r2 กำลังอ่านเนื้อหาในกระดาษอย่างตั้งใจ แล้วจู่ๆ r3 ก็เดินมาเอาปากกาขีดฆ่าข้อความทิ้งต่อหน้าต่อตา! สิ่งที่ r1 และ r2 อ่านอยู่ก็จะไม่ถูกต้องทันที

Rust เลยตัดไฟแต่ต้นลมว่า: "ถ้ามีคนอ่านอยู่ ห้ามใครมาแก้" และ "ถ้ามีคนแก้ ก็ห้ามคนอื่นอ่าน" เพื่อรับประกันว่าข้อมูลที่คุณถืออยู่ในมือนั้นถูกต้อง 100% เสมอครับ

4. ประโยชน์ของการ Borrowing

หลายคนอาจจะบ่นว่ากฎเยอะจังพี่! แต่เชื่อเถอะครับว่าถ้าคุณผ่านมันไปได้ สิ่งที่คุณจะได้กลับมาคือ Code ที่มีคุณภาพระดับโลก:

  • 🚀 Performance ที่แรงสุดขีด: เพราะเราไม่ต้องเสียเวลาและทรัพยากรไปกับการ Copy หรือ .clone() ข้อมูลขนาดใหญ่บน Heap Memory ทุกครั้งที่ส่งเข้าฟังก์ชัน การใช้ Reference ก็แค่การส่ง "ที่อยู่" (Pointer) สั้นๆ ซึ่งทำงานได้เร็วมาก

  • 🛡️ Safety ระดับสูงสุด: ระบบ Borrow Checker ของคอมไพเลอร์จะตรวจความปลอดภัยให้เราตั้งแต่ตอนเขียน ไม่ต้องรอไปลุ้นให้โปรแกรม Crash ตอนรัน (Runtime) มั่นใจได้ว่าไม่มีใครแอบแก้ข้อมูลตัดหน้ากันแน่นอน

  • 💎 Clean Code & Better DX: โค้ดของคุณจะอ่านง่ายและเป็นธรรมชาติมากขึ้น ไม่ต้องเขียนฟังก์ชันแปลกๆ ที่ต้องคอย return ค่าเดิมกลับมาเพียงเพื่อรักษา Ownership ให้วุ่นวาย


สรุป

ระบบ Borrowing คือหัวใจที่ทำให้ Rust ใช้งานได้จริงและทรงพลังครับ แม้กฎจะดูจุกจิกในช่วงแรก แต่ถ้าคุณเริ่มจับจังหวะได้ว่า "ใครคือเจ้าของ" และ "ใครเป็นแค่คนยืม" คุณจะพบว่าตัวเองกำลังเขียนโปรแกรมที่เร็วระดับเทพ ปลอดภัยจากการจารกรรมข้อมูลใน Memory และที่สำคัญคือ "Memory ไม่รั่วเลยแม้แต่ไบต์เดียว" โดยไม่ต้องง้อ Garbage Collector เลยครับ!

📌 ใน EP. ถัดไป (EP.9): เราจะไปลุยกันต่อกับเรื่อง Slices ซึ่งเป็นการยืมตข้อมูลเพียงบางส่วนมาใช้งาน (เช่น จะเอาแค่คำแรกในประโยคยาวๆ ต้องทำยังไง?) เรื่องนี้จะช่วยให้คุณจัดการ Array และ String ได้แบบโปรเฟสชันนอลยิ่งขึ้น... แล้วเจอกันครับ 🦀✨

🎯 ติดตามความรู้สาย Dev แบบสุดจัดได้ที่: ไม่อยากพลาดบทความเทคนิคเชิงลึกและอัปเดตใหม่ๆ จากทีมงาน Superdev Academy ติดตามเราได้ทุกช่องทางที่นี่ครับ:

  • 🔵 Facebook: Superdev Academy Thailand (อัปเดตข่าวสารและบทความใหม่)

  • 🎬 YouTube: Superdev Academy Channel (ติวเข้มแบบวิดีโอ)

  • 📸 Instagram: @superdevacademy (เกร็ดความรู้สั้นๆ และเบื้องหลังการทำงาน)

  • 🎬 TikTok: @superdevacademy (Tips & Tricks ฉบับย่อยง่าย)

  • 🌐 Website: superdevacademy.com (คลังบทความและคอร์สเรียนฉบับเต็ม)