Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Box<T>

Box<T> เก็บข้อมูลบน heap แทน stack เป็น smart pointer ที่ง่ายที่สุด

เมื่อไหร่ใช้ Box?

  1. Recursive types - type ที่มีตัวเองข้างใน
  2. Large data - ย้ายข้อมูลใหญ่ไป heap
  3. Trait objects - dynamic dispatch
  4. Transfer ownership โดยไม่ copy

📦 Smart Pointer Comparison

Smart Pointers Diagram


การใช้งานพื้นฐาน

fn main() {
    let b = Box::new(5);
    println!("b = {}", b);

    // Box implements Deref ใช้เหมือน reference
    let x = *b + 1;
    println!("x = {}", x);
}

Use Case 1: Recursive Types

ปัญหา: ขนาดไม่แน่นอน

#![allow(unused)]
fn main() {
// ❌ Error: recursive type has infinite size
enum List {
    Cons(i32, List),
    Nil,
}
}

Compiler ไม่รู้ว่า List ใหญ่เท่าไหร่

ทางแก้: ใช้ Box

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1,
        Box::new(Cons(2,
            Box::new(Cons(3,
                Box::new(Nil))))));

    // Print list
    print_list(&list);
}

fn print_list(list: &List) {
    match list {
        Cons(value, next) => {
            println!("{}", value);
            print_list(next);
        }
        Nil => println!("End"),
    }
}

ทำไมใช้ได้? Box มีขนาดคงที่ (pointer size) ไม่ว่าจะชี้ไปที่อะไร

Stack                    Heap
+-------------+         +-------------+
| Cons(1, ----+-------->| Cons(2, ----+--->...
+-------------+         +-------------+

Use Case 2: Trait Objects

trait Draw {
    fn draw(&self);
}

struct Circle { radius: f64 }
struct Square { side: f64 }

impl Draw for Circle {
    fn draw(&self) {
        println!("Drawing circle with radius {}", self.radius);
    }
}

impl Draw for Square {
    fn draw(&self) {
        println!("Drawing square with side {}", self.side);
    }
}

fn main() {
    // Vec ของหลาย types ที่ implement Draw
    let shapes: Vec<Box<dyn Draw>> = vec![
        Box::new(Circle { radius: 5.0 }),
        Box::new(Square { side: 3.0 }),
    ];

    for shape in &shapes {
        shape.draw();
    }
}

Use Case 3: Large Data Transfer

fn main() {
    // Large array on stack
    let big_array = [0u8; 1_000_000]; // 1MB on stack!

    // Move to heap with Box
    let boxed_array = Box::new([0u8; 1_000_000]); // 1MB on heap

    // Now ownership transfer is cheap (just move pointer)
    let another_owner = boxed_array; // Fast move!
}

Deref Trait

Box implements Deref ทำให้ใช้งานเหมือน reference:

use std::ops::Deref;

fn main() {
    let x = 5;
    let y = Box::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y); // Deref แปลง Box<T> -> T

    // Deref coercion
    let s = Box::new(String::from("hello"));
    hello(&s); // Box<String> -> &String -> &str
}

fn hello(name: &str) {
    println!("Hello, {}!", name);
}

Deref Coercion

Rust แปลง types อัตโนมัติผ่าน Deref chain:

// Box<String> -> String -> str
// &Box<String> -> &String -> &str

fn main() {
    let b = Box::new(String::from("Rust"));

    // ทั้งหมดนี้ใช้ได้เหมือนกัน
    greet(&b);           // &Box<String>
    greet(&*b);          // &String
    greet(&(*b)[..]);    // &str
}

fn greet(s: &str) {
    println!("Hello, {}!", s);
}

Drop Trait

Box จะ drop ทั้ง pointer และ data บน heap เมื่อออกจาก scope:

fn main() {
    {
        let b = Box::new(String::from("hello"));
        println!("{}", b);
    } // b dropped here → String on heap is freed

    println!("Box is dropped!");
}

Custom Drop

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("my stuff"),
    };
    let d = CustomSmartPointer {
        data: String::from("other stuff"),
    };
    println!("CustomSmartPointers created.");
}
// Output:
// CustomSmartPointers created.
// Dropping CustomSmartPointer with data `other stuff`!
// Dropping CustomSmartPointer with data `my stuff`!

Drop Early with std::mem::drop

fn main() {
    let c = Box::new(String::from("hello"));

    println!("Before drop");
    drop(c);  // Drop early
    println!("After drop");

    // println!("{}", c); // ❌ Error: c was moved
}

เปรียบเทียบ Box กับ Stack

AspectStackBox (Heap)
Sizeต้องรู้ตอน compileได้ตอน runtime
Speedเร็วกว่าช้ากว่าเล็กน้อย
OwnershipCopy หรือ MoveMove pointer เท่านั้น
Use caseSmall, fixed-sizeLarge, recursive, dynamic

ลองทำดู! 🎯

  1. สร้าง Binary Tree ด้วย Box
  2. Implement trait object vector
  3. ลอง drop Box ก่อนออกจาก scope

สรุป

แนวคิดคำอธิบาย
Box::new(x)สร้าง Box ใส่ x บน heap
*boxDeref ดึงค่าออก
Recursive typesใช้ Box ให้ขนาดคงที่
Trait objectsBox\<dyn Trait\>
Dropอัตโนมัติเมื่อออกจาก scope

👉 ต่อไป: Rc<T>