Rust Memory Management: คู่มือฉบับสมบูรณ์

บทนำ: Memory Management คืออะไร

Memory Management คือกระบวนการจัดการหน่วยความจำในโปรแกรม ซึ่งประกอบด้วย:

ปัญหาที่พบบ่อยในการจัดการหน่วยความจำ

graph TD
    A[Memory Management Problems] --> B[Memory Leaks]
    A --> C[Dangling Pointers]
    A --> D[Double Free]
    A --> E[Buffer Overflow]
    
    B --> B1[หน่วยความจำไม่ถูกคืน
ทำให้โปรแกรมกินแรมมากขึ้นเรื่อยๆ] C --> C1[ใช้ pointer ที่ชี้ไปยัง
หน่วยความจำที่ถูกคืนแล้ว] D --> D1[คืนหน่วยความจำเดิม
มากกว่าหนึ่งครั้ง] E --> E1[เขียนข้อมูลเกินขนาด
ที่จัดสรรไว้]

Memory Management ในภาษาโปรแกรมต่างๆ

1. Manual Memory Management (C)

ในภาษา C โปรแกรมเมอร์ต้องจัดการหน่วยความจำด้วยตนเองทั้งหมด

#include <stdlib.h>
#include <stdio.h>

int main() {
    // จัดสรรหน่วยความจำด้วยตัวเอง
    int* numbers = (int*)malloc(5 * sizeof(int));
    
    if (numbers == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    
    // ใช้งานหน่วยความจำ
    for (int i = 0; i < 5; i++) {
        numbers[i] = i * 10;
    }
    
    // ต้องคืนหน่วยความจำด้วยตัวเอง (ถ้าลืม = Memory Leak!)
    free(numbers);
    
    return 0;
}

ข้อดี:

ข้อเสีย:

2. RAII Pattern (C++)

C++ แนะนำ RAII (Resource Acquisition Is Initialization) pattern

#include <iostream>
#include <vector>
#include <memory>

class Resource {
private:
    int* data;
public:
    // Constructor - จัดสรรหน่วยความจำ
    Resource(int size) {
        data = new int[size];
        std::cout << "Resource acquired\n";
    }
    
    // Destructor - คืนหน่วยความจำอัตโนมัติ
    ~Resource() {
        delete[] data;
        std::cout << "Resource released\n";
    }
    
    // Rule of Five สำหรับการจัดการ copy/move
    Resource(const Resource&) = delete;
    Resource& operator=(const Resource&) = delete;
    Resource(Resource&&) = default;
    Resource& operator=(Resource&&) = default;
};

int main() {
    {
        // หน่วยความจำถูกจัดการผ่าน object lifetime
        Resource res(100);
        
        // ใช้ smart pointers สำหรับการจัดการอัตโนมัติ
        std::unique_ptr<int[]> smart_array = std::make_unique<int[]>(10);
        
        std::vector<int> vec = {1, 2, 3, 4, 5};
        // vector จัดการหน่วยความจำเองอัตโนมัติ
        
    } // Destructor ถูกเรียกอัตโนมัติเมื่อออกจาก scope
    
    return 0;
}

ข้อดี:

ข้อเสีย:

3. Garbage Collection (Java, Python, Go)

ภาษาเหล่านี้ใช้ Garbage Collector (GC) ในการจัดการหน่วยความจำอัตโนมัติ

ตัวอย่าง Java:

public class MemoryExample {
    public static void main(String[] args) {
        // จัดสรรหน่วยความจำ - Java ทำให้อัตโนมัติ
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i < 1000; i++) {
            sb.append("Hello ");
        }
        
        String result = sb.toString();
        System.out.println("Length: " + result.length());
        
        // ไม่ต้องคืนหน่วยความจำ - GC จะทำให้เอง
        // เมื่อ object ไม่มีการอ้างอิงแล้ว
    }
    
    // เมื่อ method จบ, local variables ไม่มี references
    // GC จะรีไซเคิลหน่วยความจำในภายหลัง
}

ตัวอย่าง Python:

import sys

class DataProcessor:
    def __init__(self, size):
        self.data = [0] * size
        print(f"Allocated {size} elements")
    
    def __del__(self):
        print("Object is being destroyed")

def process_data():
    # Python ใช้ reference counting + cycle detector
    processor = DataProcessor(1000)
    
    # สร้าง list
    numbers = [i for i in range(10000)]
    
    # Python GC จะจัดการหน่วยความจำเอง
    print(f"Reference count: {sys.getrefcount(processor)}")
    
    # เมื่อ function จบ, objects จะถูก GC รีไซเคิล

process_data()
# GC จะทำงานในภายหลัง (non-deterministic timing)
sequenceDiagram
    participant Program
    participant Object
    participant GC as Garbage Collector
    
    Program->>Object: สร้าง object
    Object-->>Program: reference
    Program->>Object: ใช้งาน object
    Program->>Program: ไม่ใช้ object อีก
    Note over Object: Object ไม่มี reference
    GC->>Object: Scan และ detect
    GC->>Object: Mark as garbage
    GC->>Object: Sweep (คืนหน่วยความจำ)
    Note over Object: Memory freed

ข้อดี:

ข้อเสีย:

เปรียบเทียบ Memory Management

graph LR
    A[Memory Management Approaches] --> B[Manual
C] A --> C[RAII
C++] A --> D[GC
Java/Python] A --> E[Ownership
Rust] B --> B1[Control: ★★★★★
Safety: ★☆☆☆☆
Performance: ★★★★★] C --> C1[Control: ★★★★☆
Safety: ★★★☆☆
Performance: ★★★★★] D --> D1[Control: ★★☆☆☆
Safety: ★★★★★
Performance: ★★★☆☆] E --> E1[Control: ★★★★☆
Safety: ★★★★★
Performance: ★★★★★]

Rust Ownership System

Rust แนะนำแนวคิด Ownership System ที่เป็นเอกลักษณ์ ซึ่งให้ความปลอดภัยระดับ GC แต่มี performance ใกล้เคียง manual memory management

กฎพื้นฐานของ Ownership

Rust มีกฎ 3 ข้อที่คอมไพเลอร์บังคับใช้:

  1. แต่ละค่าใน Rust มี owner เพียงคนเดียว
  2. ในเวลาหนึ่งมีได้เพียง owner เดียว
  3. เมื่อ owner ออกจาก scope ค่าจะถูกทิ้ง (drop)
graph TD
    A[Value] -->|owned by| B[Owner]
    B -->|goes out of scope| C[Value Dropped]
    C -->|memory| D[Freed Automatically]
    
    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#ffe1e1
    style D fill:#e1ffe1

ตัวอย่างที่ 1: Basic Ownership

fn main() {
    // s ไม่ valid ที่นี่ - ยังไม่ได้ประกาศ
    
    {
        let s = String::from("hello"); // s valid จากจุดนี้เป็นต้นไป
        
        // ทำอะไรกับ s ได้
        println!("{}", s);
        
    } // scope จบ, s ไม่ valid อีกต่อไป
      // Rust เรียก `drop` อัตโนมัติ
      // หน่วยความจำถูกคืนอัตโนมัติ
    
    // println!("{}", s); // ❌ ERROR! s ไม่ valid แล้ว
}

ตัวอย่างที่ 2: Move Semantics

fn main() {
    // การย้าย ownership (Move)
    let s1 = String::from("hello");
    let s2 = s1; // s1 ถูก MOVE ไปยัง s2
    
    // println!("{}", s1); // ❌ ERROR! s1 ไม่ valid อีกต่อไป
    println!("{}", s2);    // ✅ OK! s2 เป็น owner ตอนนี้
    
    // การทำงานกับ integers (Copy Types)
    let x = 5;
    let y = x; // x ถูก COPY (ไม่ใช่ move)
    
    println!("x = {}, y = {}", x, y); // ✅ OK! ทั้ง x และ y ยัง valid
}
sequenceDiagram
    participant s1
    participant Memory
    participant s2
    
    s1->>Memory: จัดสรรหน่วยความจำสำหรับ "hello"
    Note over s1,Memory: s1 เป็น owner
    
    s1->>s2: MOVE ownership
    Note over s2,Memory: s2 เป็น owner
s1 ไม่ valid อีกต่อไป Note over s1: ❌ ไม่สามารถใช้ s1 ได้ s2->>s2: ใช้งาน s2 s2->>Memory: scope จบ, คืนหน่วยความจำ

ตัวอย่างที่ 3: Ownership กับ Functions

fn main() {
    let s = String::from("hello");
    
    // s ถูก move เข้าไปใน function
    takes_ownership(s);
    
    // println!("{}", s); // ❌ ERROR! s ถูก move ไปแล้ว
    
    let x = 5;
    
    // x ถูก copy (เพราะ i32 implement Copy trait)
    makes_copy(x);
    
    println!("x = {}", x); // ✅ OK! x ยังใช้ได้
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string ออกจาก scope และถูก drop

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer ออกจาก scope แต่ไม่มีอะไรพิเศษเกิดขึ้น

ตัวอย่างที่ 4: Return Values และ Ownership

fn main() {
    let s1 = gives_ownership();        // ฟังก์ชันคืน ownership ให้ s1
    
    let s2 = String::from("hello");    // s2 เข้ามาใน scope
    
    let s3 = takes_and_gives_back(s2); // s2 ถูก move เข้าไปและ
                                       // return value ถูก move มาที่ s3
    
    println!("s1 = {}", s1);
    // println!("s2 = {}", s2); // ❌ ERROR! s2 ถูก move แล้ว
    println!("s3 = {}", s3);
    
} // s3 ออกจาก scope และถูก drop
  // s2 ถูก move แล้วดังนั้นไม่มีอะไรเกิดขึ้น
  // s1 ออกจาก scope และถูก drop

fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string // some_string ถูก return และ move ไปยัง caller
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string // a_string ถูก return และ move ไปยัง caller
}

ตัวอย่างที่ 5: Ownership กับ Structs

struct User {
    username: String,
    email: String,
    age: u32,
}

fn main() {
    let user1 = User {
        username: String::from("john_doe"),
        email: String::from("john@example.com"),
        age: 30,
    };
    
    // MOVE ทั้ง struct
    let user2 = user1;
    
    // println!("{}", user1.username); // ❌ ERROR! user1 ถูก move แล้ว
    println!("{}", user2.username);    // ✅ OK!
    
    // -----------------------------------------------
    
    // วิธีที่ดีกว่า: ใช้ Clone
    let user3 = User {
        username: String::from("jane_doe"),
        email: String::from("jane@example.com"),
        age: 25,
    };
    
    // Clone ทำสำเนาเต็มรูปแบบ
    let user4 = user3.clone();
    
    println!("{}", user3.username); // ✅ OK!
    println!("{}", user4.username); // ✅ OK!
}

Borrowing และ References

การ Borrowing คือการยืม reference โดยไม่ต้องย้าย ownership

กฎของ Borrowing

  1. ในเวลาหนึ่ง สามารถมี reference แบบใดแบบหนึ่งได้:

  2. References ต้อง valid เสมอ (ไม่มี dangling references)

graph TD
    A[Borrowing Rules] --> B[Multiple Immutable
References
&T] A --> C[ONE Mutable
Reference
&mut T] B --> B1[✅ Read-only access
✅ Thread-safe reads
✅ หลาย references พร้อมกัน] C --> C1[✅ Read-write access
❌ ต้องมีแค่ตัวเดียว
❌ ไม่มี immutable refs พร้อมกัน] style B1 fill:#e1ffe1 style C1 fill:#ffe1e1

ตัวอย่างที่ 6: Immutable References

fn main() {
    let s1 = String::from("hello");
    
    // การสร้าง immutable reference
    let len = calculate_length(&s1);
    
    println!("The length of '{}' is {}.", s1, len);
    // s1 ยังใช้ได้! เพราะเราแค่ยืม reference
}

fn calculate_length(s: &String) -> usize {
    // s เป็น reference ไปยัง String
    // ไม่ได้เป็น owner
    s.len()
} // s ออกจาก scope แต่ไม่ได้เป็น owner
  // ดังนั้นไม่มีอะไรถูก drop

อธิบาย:

ตัวอย่างที่ 7: Mutable References

fn main() {
    let mut s = String::from("hello");
    
    // สร้าง mutable reference
    change(&mut s);
    
    println!("{}", s); // Output: "hello, world"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

ตัวอย่างที่ 8: Borrowing Rules - Multiple Immutable

fn main() {
    let s = String::from("hello");
    
    // สามารถมี immutable references หลายตัวได้
    let r1 = &s;
    let r2 = &s;
    let r3 = &s;
    
    println!("{}, {}, {}", r1, r2, r3); // ✅ OK!
    
    // ทั้งหมดเป็น read-only ไม่มีการแก้ไข
    // ปลอดภัยต่อการใช้งานพร้อมกัน
}

ตัวอย่างที่ 9: Borrowing Rules - Mutable Exclusive

fn main() {
    let mut s = String::from("hello");
    
    let r1 = &mut s;
    // let r2 = &mut s; // ❌ ERROR! มี mutable reference อยู่แล้ว
    
    r1.push_str(", world");
    println!("{}", r1);
    
    // -----------------------------------------------
    
    // วิธีที่ถูกต้อง: ใช้ references ในเวลาต่างกัน
    let mut s2 = String::from("hello");
    
    {
        let r1 = &mut s2;
        r1.push_str(", world");
    } // r1 ออกจาก scope ที่นี่
    
    let r2 = &mut s2; // ✅ OK! ไม่มี r1 อีกต่อไป
    r2.push_str("!");
    println!("{}", s2);
}

ตัวอย่างที่ 10: Borrowing Rules - Mix Immutable and Mutable

fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;     // ✅ OK
    let r2 = &s;     // ✅ OK
    // let r3 = &mut s; // ❌ ERROR! มี immutable references อยู่แล้ว
    
    println!("{} and {}", r1, r2);
    // r1 และ r2 ไม่ถูกใช้หลังจากนี้
    
    let r3 = &mut s; // ✅ OK! ไม่มี immutable references แล้ว
    r3.push_str(", world");
    println!("{}", r3);
}

Non-Lexical Lifetimes (NLL): Rust compiler ฉลาดพอที่จะรู้ว่า reference ถูกใช้ตอนไหน ไม่จำเป็นต้องรอจนออกจาก scope

ตัวอย่างที่ 11: Dangling References Prevention

fn main() {
    // let reference_to_nothing = dangle();
}

// ❌ นี่คือ dangling reference - Rust ไม่อนุญาต!
// fn dangle() -> &String {
//     let s = String::from("hello");
//     &s  // คืน reference ไปยัง s
// } // s ถูก drop ที่นี่, แต่เราคืน reference!
  // Rust จะไม่ compile!

// ✅ วิธีที่ถูกต้อง: คืน ownership
fn no_dangle() -> String {
    let s = String::from("hello");
    s // MOVE ownership ไปยัง caller
}
sequenceDiagram
    participant Caller
    participant Function
    participant Memory
    
    Note over Function: สร้าง String
    Function->>Memory: จัดสรรหน่วยความจำ
    
    alt Dangling Reference (❌)
        Function-->>Caller: คืน &String
        Function->>Memory: Drop String (คืนหน่วยความจำ)
        Note over Caller: Reference ชี้ไปยัง
หน่วยความจำที่ถูกคืนแล้ว! Note over Caller: ❌ COMPILER ERROR! else Proper Ownership (✅) Function->>Caller: MOVE ownership Note over Caller: Caller เป็น owner
หน่วยความจำยัง valid Caller->>Memory: ใช้งานหน่วยความจำได้ปลอดภัย end

ตัวอย่างที่ 12: Practical Borrowing Example

fn main() {
    let mut data = vec![1, 2, 3, 4, 5];
    
    // แบบที่ไม่ดี: ย้าย ownership
    // let sum = calculate_sum(data);
    // println!("{:?}", data); // ❌ ERROR! data ถูก move
    
    // แบบที่ดี: ใช้ borrowing
    let sum = calculate_sum(&data);
    println!("Sum: {}", sum);
    println!("Data: {:?}", data); // ✅ OK! ยังใช้ data ได้
    
    // แก้ไขข้อมูล
    add_one_to_all(&mut data);
    println!("Modified data: {:?}", data);
}

fn calculate_sum(numbers: &Vec<i32>) -> i32 {
    numbers.iter().sum()
}

fn add_one_to_all(numbers: &mut Vec<i32>) {
    for num in numbers.iter_mut() {
        *num += 1; // dereference เพื่อแก้ไขค่า
    }
}

Lifetimes

Lifetime คือ scope ที่ reference มี validity Rust ใช้ lifetime annotations เพื่อให้แน่ใจว่า references valid เสมอ

ตัวอย่างที่ 13: Lifetime Basics

fn main() {
    let string1 = String::from("long string is long");
    
    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
}

// Lifetime annotation: 'a
// บอกว่า references ทั้งสองต้องมีชีวิตอย่างน้อย 'a
// และ return value จะมีชีวิตเท่ากับ 'a
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

อธิบาย Lifetime Annotation:

ตัวอย่างที่ 14: Lifetime Elision Rules

Rust มีกฎที่ช่วยให้ไม่ต้องเขียน lifetime annotations ในบางกรณี:

// ไม่ต้องใช้ lifetime annotation
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

// Rust เข้าใจว่า:
// fn first_word<'a>(s: &'a str) -> &'a str

fn main() {
    let sentence = String::from("hello world");
    let word = first_word(&sentence);
    println!("First word: {}", word);
}

Lifetime Elision Rules:

  1. แต่ละ reference parameter ได้ lifetime เป็นของตัวเอง
  2. ถ้ามี input lifetime เดียว, lifetime นั้นจะถูกกำหนดให้ output
  3. ถ้ามี &self หรือ &mut self, lifetime ของ self จะถูกกำหนดให้ output ทั้งหมด

ตัวอย่างที่ 15: Lifetimes ใน Structs

// Struct ที่เก็บ reference ต้องมี lifetime annotation
struct Article<'a> {
    title: &'a str,
    author: &'a str,
    content: &'a str,
}

impl<'a> Article<'a> {
    fn new(title: &'a str, author: &'a str, content: &'a str) -> Self {
        Article {
            title,
            author,
            content,
        }
    }
    
    fn summary(&self) -> String {
        format!("{} by {}", self.title, self.author)
    }
    
    // Method ที่คืน reference
    fn get_title(&self) -> &str {
        self.title
    }
}

fn main() {
    let title = String::from("Rust Programming");
    let author = String::from("John Doe");
    let content = String::from("Rust is awesome...");
    
    let article = Article::new(&title, &author, &content);
    
    println!("{}", article.summary());
    println!("Title: {}", article.get_title());
    
    // article, title, author, content ต้องมีชีวิตในช่วงเดียวกัน
}

ตัวอย่างที่ 16: Multiple Lifetimes

fn main() {
    let string1 = String::from("long string is long");
    let string2 = String::from("xyz");
    
    let announcement = "Today is someone's birthday!";
    let result = longest_with_announcement(
        string1.as_str(),
        string2.as_str(),
        announcement,
    );
    
    println!("Result: {}", result);
}

// Function ที่มีหลาย lifetimes
fn longest_with_announcement<'a, 'b>(
    x: &'a str,
    y: &'a str,
    ann: &'b str,
) -> &'a str {
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
graph TD
    A[Lifetime 'a] --> B[x: &'a str]
    A --> C[y: &'a str]
    A --> D[return: &'a str]
    
    E[Lifetime 'b] --> F[ann: &'b str]
    
    Note1[x และ y ต้องมีชีวิต
อย่างน้อย 'a] Note2[ann มีชีวิตแยกต่างหาก 'b] Note3[return value มีชีวิต 'a] style A fill:#e1f5ff style E fill:#ffe1e1

ตัวอย่างที่ 17: Static Lifetime

fn main() {
    // 'static lifetime = มีชีวิตตลอดทั้งโปรแกรม
    let s: &'static str = "I have a static lifetime.";
    
    println!("{}", s);
    
    // String literals มี 'static lifetime เสมอ
    // เพราะถูกเก็บใน binary ของโปรแกรม
}

// Function ที่ต้องการ static lifetime
fn get_app_name() -> &'static str {
    "My Awesome App"
}

fn main() {
    let name = get_app_name();
    println!("App name: {}", name);
}

Smart Pointers

Smart Pointers คือ data structures ที่ทำตัวเหมือน pointer แต่มี metadata และ capabilities เพิ่มเติม

ตัวอย่างที่ 18: Box - Heap Allocation

Box<T> จัดสรรข้อมูลบน heap และเป็น owner

fn main() {
    // จัดสรรข้อมูลบน heap
    let b = Box::new(5);
    println!("b = {}", b);
    
    // Use case 1: ข้อมูลขนาดใหญ่
    let large_data = Box::new([0u8; 1_000_000]);
    println!("Large data allocated on heap");
    
    // Use case 2: Recursive types
    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("List: {:?}", list);
    
    // Box ถูก drop อัตโนมัติเมื่อออกจาก scope
}
graph LR
    Stack --> Box1[Box pointer]
    Box1 -.->|points to| Heap1[Data on Heap]
    
    Stack --> Box2[Box pointer]
    Box2 -.->|points to| Heap2[Large Array
on Heap] style Stack fill:#e1f5ff style Heap1 fill:#ffe1e1 style Heap2 fill:#ffe1e1

ตัวอย่างที่ 19: Rc - Reference Counted

Rc<T> (Reference Counting) ใช้เมื่อต้องการ multiple owners

use std::rc::Rc;

fn main() {
    // สร้าง Rc - reference count = 1
    let a = Rc::new(vec![1, 2, 3]);
    println!("Count after creating a: {}", Rc::strong_count(&a));
    
    // Clone เพิ่ม reference count
    let b = Rc::clone(&a); // reference count = 2
    println!("Count after creating b: {}", Rc::strong_count(&a));
    
    {
        let c = Rc::clone(&a); // reference count = 3
        println!("Count after creating c: {}", Rc::strong_count(&a));
        println!("Vector in c: {:?}", c);
    } // c ออกจาก scope - reference count = 2
    
    println!("Count after c goes out of scope: {}", Rc::strong_count(&a));
    
    println!("Vector in a: {:?}", a);
    println!("Vector in b: {:?}", b);
    
} // a และ b ออกจาก scope - reference count = 0
  // ข้อมูลถูก drop

Use Cases สำหรับ Rc:

⚠️ ข้อจำกัด:

ตัวอย่างที่ 20: RefCell - Interior Mutability

RefCell<T> ทำให้สามารถแก้ไขข้อมูลได้แม้จะมี immutable reference

use std::cell::RefCell;

fn main() {
    let data = RefCell::new(vec![1, 2, 3]);
    
    // Borrow immutable
    {
        let borrowed = data.borrow();
        println!("Data: {:?}", *borrowed);
    } // borrow จบ
    
    // Borrow mutable
    {
        let mut borrowed_mut = data.borrow_mut();
        borrowed_mut.push(4);
        println!("Modified: {:?}", *borrowed_mut);
    } // mutable borrow จบ
    
    // ลองอีกรอบ
    println!("Final data: {:?}", data.borrow());
}

// ตัวอย่างที่เป็นประโยชน์: Mock object สำหรับ testing
struct MessengerMock {
    sent_messages: RefCell<Vec<String>>,
}

impl MessengerMock {
    fn new() -> Self {
        MessengerMock {
            sent_messages: RefCell::new(vec![]),
        }
    }
    
    fn send(&self, message: &str) {
        // แก้ไขได้แม้ &self เป็น immutable!
        self.sent_messages.borrow_mut().push(String::from(message));
    }
    
    fn get_messages(&self) -> Vec<String> {
        self.sent_messages.borrow().clone()
    }
}

fn main() {
    let messenger = MessengerMock::new();
    
    messenger.send("Hello");
    messenger.send("World");
    
    println!("Messages: {:?}", messenger.get_messages());
}

Borrowing Rules Checked at Runtime:

ตัวอย่างที่ 21: Rc<RefCell> - Multiple Owners with Mutability

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    children: RefCell<Vec<Rc<Node>>>,
}

impl Node {
    fn new(value: i32) -> Rc<Self> {
        Rc::new(Node {
            value,
            children: RefCell::new(vec![]),
        })
    }
    
    fn add_child(&self, child: Rc<Node>) {
        self.children.borrow_mut().push(child);
    }
}

fn main() {
    // สร้าง tree structure
    let root = Node::new(1);
    
    let child1 = Node::new(2);
    let child2 = Node::new(3);
    
    root.add_child(Rc::clone(&child1));
    root.add_child(Rc::clone(&child2));
    
    let grandchild = Node::new(4);
    child1.add_child(grandchild);
    
    println!("Root value: {}", root.value);
    println!("Root has {} children", root.children.borrow().len());
}
graph TD
    A[Rc RefCell T] --> B[Multiple Ownership
จาก Rc] A --> C[Interior Mutability
จาก RefCell] B --> B1[หลายตัวแปรสามารถ
เป็น owner ได้] C --> C1[แก้ไขข้อมูลได้
แม้มี immutable ref] D[Use Case] --> E[Tree structures] D --> F[Graph structures] D --> G[Shared mutable state] style A fill:#e1f5ff style D fill:#fff4e1

ตัวอย่างที่ 22: Arc - Thread-Safe Reference Counting

Arc<T> (Atomic Reference Counting) คือ Rc<T> แบบ thread-safe

use std::sync::Arc;
use std::thread;

fn main() {
    let data = Arc::new(vec![1, 2, 3, 4, 5]);
    
    let mut handles = vec![];
    
    for i in 0..3 {
        // Clone Arc สำหรับแต่ละ thread
        let data_clone = Arc::clone(&data);
        
        let handle = thread::spawn(move || {
            println!("Thread {}: {:?}", i, data_clone);
        });
        
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("Main thread: {:?}", data);
}

ตัวอย่างที่ 23: Mutex กับ Arc

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // ใช้ Mutex สำหรับ thread-safe mutation
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter_clone = Arc::clone(&counter);
        
        let handle = thread::spawn(move || {
            let mut num = counter_clone.lock().unwrap();
            *num += 1;
        });
        
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("Result: {}", *counter.lock().unwrap());
}

Smart Pointers สรุป:

Type Use Case Thread-Safe Mutable
Box<T> Heap allocation, ownership ✅ (with mut)
Rc<T> Multiple owners
Arc<T> Multiple owners, threads
RefCell<T> Interior mutability ✅ (runtime)
Mutex<T> Thread-safe mutation

Advanced Memory Patterns

ตัวอย่างที่ 24: Custom Drop Implementation

struct CustomResource {
    name: String,
}

impl CustomResource {
    fn new(name: &str) -> Self {
        println!("Creating resource: {}", name);
        CustomResource {
            name: String::from(name),
        }
    }
}

impl Drop for CustomResource {
    fn drop(&mut self) {
        println!("Dropping resource: {}", self.name);
    }
}

fn main() {
    println!("Starting main");
    
    {
        let resource1 = CustomResource::new("Resource 1");
        let resource2 = CustomResource::new("Resource 2");
        
        println!("Using resources...");
    } // resource2 และ resource1 ถูก drop ตามลำดับ (LIFO)
    
    println!("After inner scope");
    
    let resource3 = CustomResource::new("Resource 3");
    
    // Drop ก่อนเวลา
    drop(resource3);
    
    println!("Ending main");
}

Output:

Starting main
Creating resource: Resource 1
Creating resource: Resource 2
Using resources...
Dropping resource: Resource 2
Dropping resource: Resource 1
After inner scope
Creating resource: Resource 3
Dropping resource: Resource 3
Ending main

ตัวอย่างที่ 25: Preventing Memory Leaks with Weak

use std::rc::{Rc, Weak};
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

impl Node {
    fn new(value: i32) -> Rc<Self> {
        Rc::new(Node {
            value,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![]),
        })
    }
    
    fn add_child(parent: &Rc<Node>, child: &Rc<Node>) {
        // Child มี weak reference ไปยัง parent
        *child.parent.borrow_mut() = Rc::downgrade(parent);
        // Parent มี strong reference ไปยัง child
        parent.children.borrow_mut().push(Rc::clone(child));
    }
}

fn main() {
    let root = Node::new(1);
    println!("Root strong count: {}", Rc::strong_count(&root));
    println!("Root weak count: {}", Rc::weak_count(&root));
    
    {
        let child = Node::new(2);
        Node::add_child(&root, &child);
        
        println!("\nAfter adding child:");
        println!("Root strong count: {}", Rc::strong_count(&root));
        println!("Child strong count: {}", Rc::strong_count(&child));
        println!("Child weak count: {}", Rc::weak_count(&child));
        
        // ลองเข้าถึง parent จาก child
        if let Some(parent) = child.parent.borrow().upgrade() {
            println!("Child's parent value: {}", parent.value);
        }
    }
    
    println!("\nAfter child goes out of scope:");
    println!("Root strong count: {}", Rc::strong_count(&root));
    // Child ถูก drop แล้ว, ไม่มี memory leak
}
graph TD
    A[Parent Node] -->|Strong Rc| B[Child Node]
    B -.->|Weak Rc| A
    
    Note1[Strong reference
ป้องกันการ drop] Note2[Weak reference
ไม่ป้องกันการ drop
แก้ปัญหา cycle] style A fill:#e1f5ff style B fill:#ffe1e1

ตัวอย่างที่ 26: Zero-Cost Abstractions

fn main() {
    // Iterator - Zero-cost abstraction
    let numbers = vec![1, 2, 3, 4, 5];
    
    // วิธีที่ 1: Manual loop
    let mut sum1 = 0;
    for i in 0..numbers.len() {
        sum1 += numbers[i];
    }
    
    // วิธีที่ 2: Iterator (มี performance เท่ากัน!)
    let sum2: i32 = numbers.iter().sum();
    
    // วิธีที่ 3: Iterator chains
    let sum_of_squares: i32 = numbers
        .iter()
        .map(|x| x * x)
        .filter(|x| x % 2 == 0)
        .sum();
    
    println!("Sum1: {}, Sum2: {}, Sum of even squares: {}", 
             sum1, sum2, sum_of_squares);
    
    // Compiler optimizes iterator code to same assembly as manual loop!
}

ตัวอย่างที่ 27: Memory Layout Optimization

use std::mem;

// ไม่ optimize
#[repr(C)]
struct Unoptimized {
    a: u8,   // 1 byte
    b: u64,  // 8 bytes (+ 7 bytes padding)
    c: u8,   // 1 byte (+ 7 bytes padding)
}

// Optimize ด้วยการจัดเรียงใหม่
#[repr(C)]
struct Optimized {
    b: u64,  // 8 bytes
    a: u8,   // 1 byte
    c: u8,   // 1 byte (+ 6 bytes padding)
}

fn main() {
    println!("Unoptimized size: {} bytes", mem::size_of::<Unoptimized>());
    println!("Optimized size: {} bytes", mem::size_of::<Optimized>());
    
    // ตัวอย่างที่ดีกว่า: ใช้ enum
    #[derive(Debug)]
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(u8, u8, u8),
    }
    
    let msg = Message::Write(String::from("hello"));
    println!("Message size: {} bytes", mem::size_of_val(&msg));
}

สรุปและแนวทางปฏิบัติที่ดี

เปรียบเทียบ Memory Management แต่ละแบบ

graph TB
    subgraph "Manual (C)"
        A1[malloc/free]
        A2[Full Control]
        A3[High Risk]
    end
    
    subgraph "RAII (C++)"
        B1[Destructors]
        B2[Scoped Lifetime]
        B3[Still Risky]
    end
    
    subgraph "GC (Java/Python)"
        C1[Automatic]
        C2[Safe]
        C3[Runtime Overhead]
    end
    
    subgraph "Ownership (Rust)"
        D1[Compile-Time]
        D2[Safe]
        D3[Zero Overhead]
    end
    
    style D1 fill:#e1ffe1
    style D2 fill:#e1ffe1
    style D3 fill:#e1ffe1

แนวทางปฏิบัติที่ดีสำหรับ Rust

1. ใช้ Ownership Correctly

// ❌ ไม่ดี: Clone มากเกินไป
fn process_bad(data: Vec<i32>) -> Vec<i32> {
    let copy = data.clone(); // ไม่จำเป็น!
    copy.iter().map(|x| x * 2).collect()
}

// ✅ ดี: ใช้ ownership อย่างเหมาะสม
fn process_good(data: Vec<i32>) -> Vec<i32> {
    data.into_iter().map(|x| x * 2).collect()
}

// ✅ ดีกว่า: ถ้าต้องการเก็บ data เดิมไว้ ใช้ reference
fn process_better(data: &[i32]) -> Vec<i32> {
    data.iter().map(|x| x * 2).collect()
}

2. เลือก Smart Pointer ที่เหมาะสม

use std::rc::Rc;
use std::sync::Arc;

// Single-threaded: ใช้ Rc<T>
fn single_threaded_example() {
    let data = Rc::new(vec![1, 2, 3]);
    let data2 = Rc::clone(&data);
    // ใช้งานทั้ง data และ data2
}

// Multi-threaded: ใช้ Arc<T>
fn multi_threaded_example() {
    let data = Arc::new(vec![1, 2, 3]);
    
    let data_clone = Arc::clone(&data);
    std::thread::spawn(move || {
        println!("{:?}", data_clone);
    });
}

3. หลีกเลี่ยง Lifetime Complexity

// ❌ ซับซ้อนเกินไป
struct Complex<'a, 'b, 'c> {
    field1: &'a str,
    field2: &'b str,
    field3: &'c str,
}

// ✅ ใช้ owned types แทน
struct Simple {
    field1: String,
    field2: String,
    field3: String,
}

// หรือถ้าจำเป็นต้องใช้ references ใช้ lifetime เดียว
struct Simpler<'a> {
    field1: &'a str,
    field2: &'a str,
    field3: &'a str,
}

4. ใช้ Borrowing มากกว่า Cloning

// ❌ Clone ทุกครั้ง
fn bad_example() {
    let data = vec![1, 2, 3, 4, 5];
    
    let sum = calculate_sum(data.clone());
    let avg = calculate_average(data.clone());
    let max = find_max(data.clone());
}

// ✅ ใช้ references
fn good_example() {
    let data = vec![1, 2, 3, 4, 5];
    
    let sum = calculate_sum(&data);
    let avg = calculate_average(&data);
    let max = find_max(&data);
    
    // data ยังใช้งานได้
}

fn calculate_sum(data: &[i32]) -> i32 {
    data.iter().sum()
}

fn calculate_average(data: &[i32]) -> f64 {
    data.iter().sum::<i32>() as f64 / data.len() as f64
}

fn find_max(data: &[i32]) -> Option<i32> {
    data.iter().max().copied()
}

จุดแข็งของ Rust Memory Management

  1. Memory Safety Without GC: ปลอดภัยโดยไม่ต้องใช้ garbage collector
  2. Zero-Cost Abstractions: ไม่มี runtime overhead
  3. Compile-Time Guarantees: จับ bugs ตั้งแต่ compile time
  4. Predictable Performance: ไม่มี GC pause
  5. Fearless Concurrency: Thread-safe โดย design

สรุปสุดท้าย

%%{init: {'theme':'base', 'themeVariables': {'primaryTextColor':'#ebdbb2', 'secondaryTextColor':'#ebdbb2', 'fontSize':'16px', 'fontFamily':'arial'}}}%%
mindmap
  root((Rust Memory
Management)) Ownership One owner Move semantics Automatic drop Borrowing Immutable refs Mutable refs Lifetime checks Smart Pointers Box heap Rc shared Arc threads RefCell runtime Benefits Memory safe No GC Fast Concurrent

Rust Memory Management เป็นระบบที่ทรงพลังที่สุดในบรรดาภาษาโปรแกรมสมัยใหม่ โดยให้ความปลอดภัยระดับเดียวกับ garbage collected languages แต่มี performance ใกล้เคียงกับ manual memory management ผ่านระบบ ownership, borrowing, และ lifetimes ที่ตรวจสอบโดย compiler

Key Takeaways:

Happy Rust Programming! 🦀