🎯 Methods: Hàm Bên Trong Struct
🎯 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 được method là gì và khác gì với function
- ✅ Định nghĩa methods với
implblocks - ✅ Sử dụng
&self,&mut self, vàself - ✅ Tạo associated functions (như constructors)
- ✅ Sử dụng nhiều impl blocks
- ✅ Áp dụng method chaining
🤔 Method Là Gì?
Ẩn Dụ Cuộc Sống: Điều Khiển Từ Xa
Method giống như các nút trên điều khiển TV:
📺 Điều Khiển TV:
- Mỗi nút là một hành động: Bật/tắt, Tăng âm, Đổi kênh
- Nút gắn liền với TV, không thể dùng cho tủ lạnh
- Các nút "biết" cách điều khiển TV
🎯 Methods Trong Rust:
- Hàm gắn liền với struct
- Truy cập qua dot notation:
rect.area() - Tham số đầu tiên là
self(instance)
Method vs Function
Function:
fn calculate_area(rect: &Rectangle) -> u32 {
rect.width * rect.height
}
// Gọi
let area = calculate_area(&rect);
Method:
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
// Gọi
let area = rect.area();
Lợi ích của methods:
- ✅ Gọn gàng:
rect.area()thay vìcalculate_area(&rect) - ✅ Tổ chức: Tất cả logic của Rectangle ở một chỗ
- ✅ Namespacing: Nhiều structs có thể có method
area()
🛠️ Impl Blocks
Cú Pháp Cơ Bản
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle {
width: 30,
height: 50,
};
println!("Diện tích: {}", rect.area());
}
Giải thích:
impl Rectangle→ Implementation block cho Rectanglefn area(&self)→ Method với tham số đầu là&selfself.width→ Truy cập field của instance
🔑 self, &self, và &mut self
&self - Immutable Borrow
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
println!("Area: {}", rect1.area());
println!("Can hold: {}", rect1.can_hold(&rect2));
// rect1 vẫn dùng được
println!("{:?}", rect1);
}
Khi nào dùng &self:
- Chỉ đọc dữ li ệu
- Không thay đổi instance
- Cho phép gọi nhiều lần
&mut self - Mutable Borrow
impl Rectangle {
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
fn set_width(&mut self, width: u32) {
self.width = width;
}
}
fn main() {
let mut rect = Rectangle { width: 10, height: 20 };
println!("Trước: {:?}", rect);
rect.scale(2);
println!("Sau scale: {:?}", rect);
rect.set_width(100);
println!("Sau set_width: {:?}", rect);
}
Khi nào dùng &mut self:
- Cần sửa đổi instance
- Instance phải là
mut
self - Take Ownership
impl Rectangle {
fn into_square(self) -> Rectangle {
let side = self.width.min(self.height);
Rectangle {
width: side,
height: side,
}
}
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
let square = rect.into_square();
// ❌ rect không dùng được nữa - đã bị consume
// println!("{:?}", rect);
println!("Square: {:?}", square);
}
Khi nào dùng self:
- Transform instance thành giá trị khác
- Consume instance
- Hiếm khi dùng
🏭 Associated Functions
Functions không có self parameter:
impl Rectangle {
// Associated function (không có self)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
// Constructor
fn new(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
}
fn main() {
// Gọi với ::
let sq = Rectangle::square(20);
let rect = Rectangle::new(10, 30);
println!("Square: {:?}", sq);
println!("Rect: {:?}", rect);
}
Khi nào dùng:
- ✅ Constructors
- ✅ Factory methods
- ✅ Utility functions liên quan đến type
🔗 Method Chaining
Methods return &mut self để chain:
impl Rectangle {
fn set_width(&mut self, width: u32) -> &mut Self {
self.width = width;
self
}
fn set_height(&mut self, height: u32) -> &mut Self {
self.height = height;
self
}
fn scale(&mut self, factor: u32) -> &mut Self {
self.width *= factor;
self.height *= factor;
self
}
}
fn main() {
let mut rect = Rectangle::new(10, 20);
// Method chaining
rect.set_width(30)
.set_height(40)
.scale(2);
println!("{:?}", rect);
}
🎯 Ví Dụ Thực Tế
Ví Dụ 1: Circle
#[derive(Debug)]
struct Circle {
radius: f64,
}
impl Circle {
fn new(radius: f64) -> Circle {
Circle { radius }
}
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn circumference(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
fn scale(&mut self, factor: f64) {
self.radius *= factor;
}
}
fn main() {
let mut circle = Circle::new(5.0);
println!("Radius: {}", circle.radius);
println!("Area: {:.2}", circle.area());
println!("Circumference: {:.2}", circle.circumference());
circle.scale(2.0);
println!("After scale: {:?}", circle);
}
Đầu ra:
Radius: 5
Area: 78.54
Circumference: 31.42
After scale: Circle { radius: 10.0 }
Ví Dụ 2: BankAccount
#[derive(Debug)]
struct BankAccount {
owner: String,
balance: f64,
}
impl BankAccount {
fn new(owner: String) -> BankAccount {
BankAccount {
owner,
balance: 0.0,
}
}
fn deposit(&mut self, amount: f64) {
if amount > 0.0 {
self.balance += amount;
println!("Nạp {} thành công", amount);
}
}
fn withdraw(&mut self, amount: f64) -> bool {
if amount > 0.0 && self.balance >= amount {
self.balance -= amount;
println!("Rút {} thành công", amount);
true
} else {
println!("Rút tiền thất bại!");
false
}
}
fn get_balance(&self) -> f64 {
self.balance
}
}
fn main() {
let mut account = BankAccount::new(String::from("An"));
account.deposit(1000.0);
account.deposit(500.0);
account.withdraw(300.0);
account.withdraw(2000.0); // Thất bại
println!("Số dư: {}", account.get_balance());
}
Đầu ra:
Nạp 1000 thành công
Nạp 500 thành công
Rút 300 thành công
Rút tiền thất bại!
Số dư: 1200
Ví Dụ 3: Counter
#[derive(Debug)]
struct Counter {
count: i32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
fn increment(&mut self) -> &mut Self {
self.count += 1;
self
}
fn decrement(&mut self) -> &mut Self {
self.count -= 1;
self
}
fn reset(&mut self) -> &mut Self {
self.count = 0;
self
}
fn get(&self) -> i32 {
self.count
}
}
fn main() {
let mut counter = Counter::new();
counter
.increment()
.increment()
.increment()
.decrement()
.increment();
println!("Count: {}", counter.get());
counter.reset();
println!("After reset: {}", counter.get());
}
Đầu ra:
Count: 3
After reset: 0