🔐 RefCell: Interior Mutability
🎯 Mục Tiêu Bài Học
Sau khi hoàn thành bài học này, bạn sẽ:
- ✅ Hiểu interior mutability pattern
- ✅ Sử dụng
RefCell<T>để mutate immutable data - ✅ Biết
.borrow()và.borrow_mut() - ✅ Hiểu runtime borrow checking
- ✅ Kết hợp
Rc<RefCell<T>>pattern - ✅ Tránh runtime panics
🤔 RefCell Là Gì?
Ẩn Dụ Cuộc Sống: Két Sắt Thông Minh
RefCell giống như két sắt có hệ thống check-in/check-out:
🏦 Két Sắt Thông Minh:
- Bên ngoài: Két không di chuyển được (immutable)
- Bên trong: Đồ có thể thay đổi
- Check-in/out: Theo dõi ai đang dùng
- Runtime check: Kiểm tra khi mở két
🦀 RefCell Trong Rust:
- Immutable reference bên ngoài
- Interior mutability bên trong
- Runtime borrow checking
- Panic nếu vi phạm rules
Ví Dụ Cơ Bản
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
// Borrow immutable
let borrowed = data.borrow();
println!("Value: {}", *borrowed);
drop(borrowed); // Release borrow
// Borrow mutable
let mut borrowed_mut = data.borrow_mut();
*borrowed_mut += 10;
drop(borrowed_mut); // Release borrow
println!("New value: {}", data.borrow());
}
📊 Interior Mutability
Vấn Đề Cần Giải Quyết
// ❌ Compile error!
fn main() {
let x = 5;
// x.mutate(); // ERROR: x is immutable
let vec = vec![1, 2, 3];
// vec.push(4); // ERROR: vec is immutable
}
Rust's borrowing rules (compile-time):
- Hoặc một
&mut T - Hoặc nhiều
&T
Giải Pháp: RefCell
use std::cell::RefCell;
fn main() {
let data = RefCell::new(vec![1, 2, 3]);
// Immutable reference to RefCell
// But can mutate INTERIOR!
data.borrow_mut().push(4);
println!("{:?}", data.borrow()); // [1, 2, 3, 4]
}
🔧 Sử Dụng RefCell
.borrow() - Immutable Borrow
use std::cell::RefCell;
fn main() {
let data = RefCell::new(String::from("hello"));
// Immutable borrow
let borrowed1 = data.borrow();
let borrowed2 = data.borrow(); // ✅ OK: nhiều immutable borrows
println!("{}, {}", borrowed1, borrowed2);
}
.borrow_mut() - Mutable Borrow
use std::cell::RefCell;
fn main() {
let data = RefCell::new(String::from("hello"));
{
let mut borrowed = data.borrow_mut();
borrowed.push_str(" world");
} // borrowed dropped here
println!("{}", data.borrow()); // "hello world"
}
Runtime Panic
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
let borrow1 = data.borrow_mut();
// ❌ PANIC at runtime!
// let borrow2 = data.borrow_mut(); // Already mutably borrowed!
drop(borrow1); // Release first
let borrow2 = data.borrow_mut(); // ✅ OK now
}
.try_borrow() - Safe Alternative
use std::cell::RefCell;
fn main() {
let data = RefCell::new(5);
let borrow1 = data.borrow_mut();
// Try borrow - returns Result
match data.try_borrow() {
Ok(b) => println!("Borrowed: {}", b),
Err(e) => println!("Cannot borrow: {:?}", e),
}
drop(borrow1);
match data.try_borrow() {
Ok(b) => println!("Borrowed: {}", b), // ✅ Success
Err(e) => println!("Cannot borrow: {:?}", e),
}
}
🔗 Rc<RefCell<T>> Pattern
Vấn Đề: Shared Mutable State
use std::rc::Rc;
fn main() {
let data = Rc::new(vec![1, 2, 3]);
let data1 = Rc::clone(&data);
let data2 = Rc::clone(&data);
// ❌ Cannot mutate! Rc is immutable
// data1.push(4); // ERROR!
}
Giải Pháp: Rc<RefCell<T>>
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let data = Rc::new(RefCell::new(vec![1, 2, 3]));
let data1 = Rc::clone(&data);
let data2 = Rc::clone(&data);
// ✅ Can mutate interior!
data1.borrow_mut().push(4);
data2.borrow_mut().push(5);
println!("{:?}", data.borrow()); // [1, 2, 3, 4, 5]
}
Shared Mutable Graph
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: &Rc<Self>, child: Rc<Node>) {
self.children.borrow_mut().push(child);
}
}
fn main() {
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));
println!("Root has {} children", root.children.borrow().len()); // 2
}
🎯 Ví Dụ Thực Tế
Ví Dụ 1: Mock Object (Testing)
use std::cell::RefCell;
struct MockDatabase {
calls: RefCell<Vec<String>>,
}
impl MockDatabase {
fn new() -> Self {
MockDatabase {
calls: RefCell::new(vec![]),
}
}
fn query(&self, sql: &str) -> String {
// Track calls (mutate interior)
self.calls.borrow_mut().push(sql.to_string());
format!("Result for: {}", sql)
}
fn call_count(&self) -> usize {
self.calls.borrow().len()
}
}
fn main() {
let db = MockDatabase::new();
db.query("SELECT * FROM users");
db.query("SELECT * FROM products");
println!("Database called {} times", db.call_count()); // 2
}
Ví Dụ 2: Tree với Parent References
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(self: &Rc<Self>, child: &Rc<Node>) {
child.parent.borrow_mut().clone_from(&Rc::downgrade(self));
self.children.borrow_mut().push(Rc::clone(child));
}
}
fn main() {
let root = Node::new(1);
let child = Node::new(2);
root.add_child(&child);
println!("Root has {} children", root.children.borrow().len());
if let Some(parent) = child.parent.borrow().upgrade() {
println!("Child's parent: {}", parent.value);
}
}