Giao diện
Smart Pointers & Memory Management Deep Tech
Quản lý Memory phức tạp một cách an toàn
Box<T> - Heap Allocation
Cơ bản
Box<T> allocate T trên heap thay vì stack:
rust
fn main() {
// Stack allocation
let x: i32 = 5; // 4 bytes on stack
// Heap allocation với Box
let boxed: Box<i32> = Box::new(5); // pointer on stack, i32 on heap
// Dereference để access value
println!("Value: {}", *boxed);
}Memory Layout
STACK HEAP
┌─────────────────┐ ┌─────────────────┐
│ boxed: Box<i32> │───────────────▶│ 5 │
│ (8 bytes ptr) │ │ (4 bytes i32) │
└─────────────────┘ └─────────────────┘Use Cases
rust
// 1. Recursive types (size unknown at compile time)
enum List {
Cons(i32, Box<List>),
Nil,
}
let list = List::Cons(1,
Box::new(List::Cons(2,
Box::new(List::Cons(3,
Box::new(List::Nil))))));
// 2. Large data - avoid stack overflow
struct LargeData {
buffer: [u8; 1_000_000], // 1MB
}
// ❌ Stack overflow risk
// let data = LargeData { buffer: [0; 1_000_000] };
// ✅ Heap allocation
let data = Box::new(LargeData { buffer: [0; 1_000_000] });
// 3. Trait objects (dyn Trait)
let handler: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);Box Internals
rust
// Box is essentially:
#[repr(transparent)]
pub struct Box<T: ?Sized>(Unique<T>);
// When dropped, Box:
// 1. Calls T::drop() if T implements Drop
// 2. Deallocates heap memoryRc<T> - Reference Counting
Shared Ownership (Single-threaded)
Rc<T> cho phép nhiều owners cho cùng một data:
rust
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("hello"));
println!("Count after a: {}", Rc::strong_count(&a)); // 1
let b = Rc::clone(&a); // Increment count, NOT deep clone
println!("Count after b: {}", Rc::strong_count(&a)); // 2
{
let c = Rc::clone(&a);
println!("Count in scope: {}", Rc::strong_count(&a)); // 3
}
println!("Count after scope: {}", Rc::strong_count(&a)); // 2
// Data freed when count reaches 0
}Memory Layout
STACK HEAP
┌───────┐ ┌─────────────────────────┐
a │ Rc ───┼─────────────────▶│ strong_count: 2 │
└───────┘ ├─────────────────────────┤
│ weak_count: 1 │
┌───────┐ ├─────────────────────────┤
b │ Rc ───┼─────────────────▶│ data: "hello" │
└───────┘ └─────────────────────────┘
(Cả a và b trỏ đến CÙNG allocation)Reference Counting Code
rust
// Simplified Rc internals
struct RcBox<T> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
impl<T> Clone for Rc<T> {
fn clone(&self) -> Rc<T> {
// Increment count (NOT atomic, single-thread only)
self.inner().strong.set(self.inner().strong.get() + 1);
Rc { ptr: self.ptr }
}
}
impl<T> Drop for Rc<T> {
fn drop(&mut self) {
// Decrement count
let count = self.inner().strong.get() - 1;
self.inner().strong.set(count);
if count == 0 {
// Drop the value
unsafe { drop_in_place(&mut self.inner().value); }
// Deallocate if no weak refs
if self.inner().weak.get() == 0 {
dealloc(self.ptr);
}
}
}
}⚠️ KHÔNG THREAD-SAFE
Rc<T> sử dụng non-atomic operations. KHÔNG được share qua threads:
rust
use std::rc::Rc;
use std::thread;
let data = Rc::new(42);
// thread::spawn(move || {
// println!("{}", data); // ERROR: Rc<i32> is not Send
// });Arc<T> - Atomic Reference Counting
Thread-safe Shared Ownership
Arc<T> = Rc<T> + atomic operations:
rust
use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for i in 0..3 {
let data = Arc::clone(&data); // Atomic increment
handles.push(thread::spawn(move || {
println!("Thread {}: {:?}", i, data);
}));
}
for handle in handles {
handle.join().unwrap();
}
}Atomic Operations
rust
// Arc uses atomic operations (simplified)
impl<T> Clone for Arc<T> {
fn clone(&self) -> Arc<T> {
// Atomic increment - thread-safe
self.inner().strong.fetch_add(1, Ordering::Relaxed);
Arc { ptr: self.ptr }
}
}
impl<T> Drop for Arc<T> {
fn drop(&mut self) {
// Atomic decrement with proper ordering
if self.inner().strong.fetch_sub(1, Ordering::Release) != 1 {
return;
}
// Synchronize before accessing data
atomic::fence(Ordering::Acquire);
// Now safe to drop
unsafe { drop_in_place(&mut self.inner().data); }
}
}Performance: Rc vs Arc
rust
// Benchmark: 10M clone + drop operations
// Rc: ~150ms (non-atomic increment/decrement)
// Arc: ~450ms (atomic operations, cache synchronization)
// Rule: Use Rc for single-threaded, Arc only when neededRefCell<T> - Interior Mutability
Runtime Borrow Checking
RefCell<T> cho phép mutable borrow của data bên trong &T:
rust
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// Immutable borrow
{
let borrowed = data.borrow();
println!("Value: {}", *borrowed);
}
// Mutable borrow
{
let mut borrowed = data.borrow_mut();
*borrowed += 10;
}
println!("Final: {:?}", data); // RefCell { value: 15 }
}Borrow Rules at Runtime
rust
use std::cell::RefCell;
fn main() {
let data = RefCell::new(vec![1, 2, 3]);
let borrow1 = data.borrow(); // OK: first immutable borrow
let borrow2 = data.borrow(); // OK: multiple immutable borrows
// ❌ PANIC at runtime!
// let mut_borrow = data.borrow_mut(); // Can't mut borrow while immutable exists
drop(borrow1);
drop(borrow2);
let mut_borrow = data.borrow_mut(); // OK now
}RefCell Internals
rust
// Simplified RefCell
pub struct RefCell<T> {
borrow: Cell<isize>, // >0: shared borrows, -1: exclusive borrow
value: UnsafeCell<T>,
}
impl<T> RefCell<T> {
pub fn borrow(&self) -> Ref<T> {
match self.borrow.get() {
b if b >= 0 => {
self.borrow.set(b + 1); // Increment shared count
Ref { ... }
}
_ => panic!("already mutably borrowed"),
}
}
pub fn borrow_mut(&self) -> RefMut<T> {
match self.borrow.get() {
0 => {
self.borrow.set(-1); // Mark as exclusively borrowed
RefMut { ... }
}
_ => panic!("already borrowed"),
}
}
}Common Pattern: Rc + RefCell
rust
use std::cell::RefCell;
use std::rc::Rc;
// Shared mutable state (single-threaded)
struct Node {
value: i32,
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let parent = Rc::new(Node {
value: 1,
children: RefCell::new(vec![]),
});
let child = Rc::new(Node {
value: 2,
children: RefCell::new(vec![]),
});
// Mutate through shared reference
parent.children.borrow_mut().push(Rc::clone(&child));
}Weak<T> - Breaking Cycles
Reference Cycle Problem
rust
use std::cell::RefCell;
use std::rc::Rc;
struct Node {
value: i32,
parent: RefCell<Option<Rc<Node>>>, // ❌ Creates cycle!
children: RefCell<Vec<Rc<Node>>>,
}
// parent → child → parent → ... (memory leak!)Solution: Weak References
rust
use std::cell::RefCell;
use std::rc::{Rc, Weak};
struct Node {
value: i32,
parent: RefCell<Weak<Node>>, // ✅ Weak doesn't count
children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
let parent = Rc::new(Node {
value: 1,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
let child = Rc::new(Node {
value: 2,
parent: RefCell::new(Rc::downgrade(&parent)), // Weak ref to parent
children: RefCell::new(vec![]),
});
parent.children.borrow_mut().push(Rc::clone(&child));
// Access parent from child
if let Some(parent_rc) = child.parent.borrow().upgrade() {
println!("Parent value: {}", parent_rc.value);
}
}Weak Behavior
rust
use std::rc::{Rc, Weak};
fn main() {
let weak: Weak<i32>;
{
let strong = Rc::new(42);
weak = Rc::downgrade(&strong);
// upgrade() returns Some while strong exists
assert!(weak.upgrade().is_some());
}
// strong dropped, data deallocated
// upgrade() returns None after data gone
assert!(weak.upgrade().is_none());
}Interior Mutability Patterns
Cell vs RefCell
rust
use std::cell::{Cell, RefCell};
// Cell<T>: Copy in, Copy out (T must be Copy)
let cell = Cell::new(5);
cell.set(10);
let value = cell.get(); // Returns copy
// RefCell<T>: Borrow checking at runtime
let refcell = RefCell::new(vec![1, 2, 3]);
refcell.borrow_mut().push(4); // Borrow and modifyOnceCell & LazyCell
rust
use std::cell::OnceCell;
// Initialize once, use many times
static CONFIG: OnceCell<Config> = OnceCell::new();
fn get_config() -> &'static Config {
CONFIG.get_or_init(|| {
Config::load_from_file()
})
}Comparison Table
| Type | Ownership | Thread-safe | Mutability | Use Case |
|---|---|---|---|---|
Box<T> | Single | Same as T | Via &mut | Heap allocation |
Rc<T> | Shared | ❌ | Immutable | Single-thread sharing |
Arc<T> | Shared | ✅ | Immutable | Multi-thread sharing |
Cell<T> | Single | ❌ | Copy in/out | Simple interior mut |
RefCell<T> | Single | ❌ | Runtime borrow | Complex interior mut |
Mutex<T> | Shared | ✅ | Lock-based | Thread-safe mutation |
Bảng Tóm tắt
rust
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::cell::{Cell, RefCell};
// === HEAP ALLOCATION ===
let boxed = Box::new(42); // Single owner, heap
// === SHARED OWNERSHIP ===
let rc = Rc::new(42); // Single-thread shared
let rc2 = Rc::clone(&rc); // Increment count
let arc = Arc::new(42); // Thread-safe shared
let arc2 = Arc::clone(&arc); // Atomic increment
// === WEAK REFERENCES ===
let weak = Rc::downgrade(&rc); // Doesn't prevent dealloc
let strong = weak.upgrade(); // Returns Option<Rc<T>>
// === INTERIOR MUTABILITY ===
let cell = Cell::new(42);
cell.set(100); // Copy-based
let refcell = RefCell::new(vec![]);
refcell.borrow_mut().push(1); // Runtime borrow check
// === COMMON PATTERNS ===
Rc<RefCell<T>> // Single-thread shared mutation
Arc<Mutex<T>> // Multi-thread shared mutation