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[เขียนข้อมูลเกินขนาด
ที่จัดสรรไว้]
ในภาษา 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;
}
ข้อดี:
ข้อเสีย:
malloc() และ free()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;
}
ข้อดี:
ข้อเสีย:
ภาษาเหล่านี้ใช้ Garbage Collector (GC) ในการจัดการหน่วยความจำอัตโนมัติ
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 จะรีไซเคิลหน่วยความจำในภายหลัง
}
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
ข้อดี:
ข้อเสีย:
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 ที่เป็นเอกลักษณ์ ซึ่งให้ความปลอดภัยระดับ GC แต่มี performance ใกล้เคียง manual memory management
Rust มีกฎ 3 ข้อที่คอมไพเลอร์บังคับใช้:
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
fn main() {
// s ไม่ valid ที่นี่ - ยังไม่ได้ประกาศ
{
let s = String::from("hello"); // s valid จากจุดนี้เป็นต้นไป
// ทำอะไรกับ s ได้
println!("{}", s);
} // scope จบ, s ไม่ valid อีกต่อไป
// Rust เรียก `drop` อัตโนมัติ
// หน่วยความจำถูกคืนอัตโนมัติ
// println!("{}", s); // ❌ ERROR! s ไม่ valid แล้ว
}
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 จบ, คืนหน่วยความจำ
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 แต่ไม่มีอะไรพิเศษเกิดขึ้น
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
}
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 คือการยืม reference โดยไม่ต้องย้าย ownership
ในเวลาหนึ่ง สามารถมี reference แบบใดแบบหนึ่งได้:
&T) หรือ&mut T)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
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
อธิบาย:
&s1 สร้าง reference ที่ชี้ไปยัง s1 โดยไม่ย้าย ownership&String แทนที่จะเป็น Strings1 ยังคงเป็น ownerfn 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");
}
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 ไม่มีการแก้ไข
// ปลอดภัยต่อการใช้งานพร้อมกัน
}
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);
}
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
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
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 เพื่อแก้ไขค่า
}
}
Lifetime คือ scope ที่ reference มี validity Rust ใช้ lifetime annotations เพื่อให้แน่ใจว่า references valid เสมอ
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:
'a คือชื่อของ lifetime<'a> ประกาศ lifetime parameter&'a str หมายความว่า reference นี้มีชีวิต 'aRust มีกฎที่ช่วยให้ไม่ต้องเขียน 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:
&self หรือ &mut self, lifetime ของ self จะถูกกำหนดให้ output ทั้งหมด// 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 ต้องมีชีวิตในช่วงเดียวกัน
}
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
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 คือ data structures ที่ทำตัวเหมือน pointer แต่มี metadata และ capabilities เพิ่มเติม
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
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
⚠️ ข้อจำกัด:
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:
borrow() สำหรับ immutable borrowborrow_mut() สำหรับ mutable borrowuse 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
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);
}
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 | ✅ | ✅ |
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
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
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!
}
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));
}
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
// ❌ ไม่ดี: 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()
}
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);
});
}
// ❌ ซับซ้อนเกินไป
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,
}
// ❌ 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()
}
%%{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! 🦀