📥 Tham Số Hàm: Truyền Dữ Liệu Vào
Mục Tiêu Bài Học
Sau bài này, bạn sẽ:
- ✅ Hiểu cách truyền nhiều parameters
- ✅ Biết sự khác biệt giữa pass by value và reference
- ✅ Sử dụng mutable parameters
- ✅ Tránh lỗi ownership khi truyền tham số
🤔 Parameters Là Gì?
Parameters (tham số) là đầu vào của function - cách để truyền dữ liệu vào function.
fn chao(ten: &str) { // ten là parameter
println!("Xin chào, {}!", ten);
}
fn main() {
chao("An"); // "An" là argument
}
Ẩn Dụ
Hãy tưởng tượng parameters như nguyên liệu đầu vào cho máy móc:
🏗️ Máy sản xuất gạch:
- Parameters: Xi măng, cát, nước (nguyên liệu)
- Process: Trộn, đúc, ép
- Output: Gạch hoàn thiện
Đặc điểm:
- Máy cần biết chính xác nguyên liệu (kiểu dữ liệu)
- Không xi măng → Máy không chạy được
- Sai tỷ lệ → Gạch kém chất lượng 🛡️
📝 Một Parameter
Cú pháp đầy đủ:
fn function_name(parameter_name: Type) {
// Code
}
Ví Dụ
fn in_boi_so(n: i32) {
println!("Bội số của {}:", n);
for i in 1..=5 {
println!("{} × {} = {}", n, i, n * i);
}
}
fn main() {
in_boi_so(7);
}
Kết quả:
Bội số của 7:
7 × 1 = 7
7 × 2 = 14
7 × 3 = 21
7 × 4 = 28
7 × 5 = 35
🔢 Nhiều Parameters
Cách viết nhiều parameters, cách nhau bằng dấu phẩy:
fn tinh_dien_tich(width: i32, height: i32) {
let area = width * height;
println!("Diện tích {}×{} = {}", width, height, area);
}
fn main() {
tinh_dien_tich(10, 20);
tinh_dien_tich(5, 8);
}
Kết quả:
Diện tích 10×20 = 200
Diện tích 5×8 = 40
Nhiều Kiểu Khác Nhau
fn thong_tin_sinh_vien(ten: &str, tuoi: i32, diem: f64) {
println!("📝 Sinh viên: {}", ten);
println!("🎂 Tuổi: {}", tuoi);
println!("📊 Điểm: {:.2}", diem);
}
fn main() {
thong_tin_sinh_vien("An", 20, 8.5);
}
Kết quả:
📝 Sinh viên: An
🎂 Tuổi: 20
📊 Điểm: 8.50
🔄 Pass By Value (Chuyển Giá Trị)
Với kiểu đơn giản (Copy types), Rust copy giá trị:
fn nhan_doi(mut x: i32) {
x *= 2;
println!("Trong function: x = {}", x);
}
fn main() {
let so = 5;
println!("Trước: so = {}", so);
nhan_doi(so);
println!("Sau: so = {}", so); // ✅ Vẫn là 5!
}
Kết quả:
Trước: so = 5
Trong function: x = 10
Sau: so = 5
Giải thích:
nhan_doi(so)→ Copy giá trị 5- Thay đổi
xtrong function không ảnh hưởngsobên ngoài - Các kiểu Copy: i32, f64, bool, char, ...
📦 Pass By Reference (Mượn Tham Chiếu)
Với kiểu phức tạp (String, Vec, ...), dùng reference để mượn:
Immutable Reference &T
fn in_thong_tin(text: &String) {
println!("Độ dài: {}", text.len());
println!("Nội dung: {}", text);
}
fn main() {
let message = String::from("Hello Rust!");
in_thong_tin(&message); // ✅ Mượn
println!("Vẫn dùng được: {}", message); // ✅ OK
}
Kết quả:
Độ dài: 11
Nội dung: Hello Rust!
Vẫn dùng được: Hello Rust!
Giải thích:
&message→ Mượn reference (không chuyển ownership)- Function chỉ đọc, không thay đổi
- Sau function,
messagevẫn dùng được
Mutable Reference &mut T
fn them_cam_tan(text: &mut String) {
text.push_str("!!!");
}
fn main() {
let mut message = String::from("Hello");
println!("Trước: {}", message);
them_cam_tan(&mut message); // ✅ Mượn mutable
println!("Sau: {}", message);
}
Kết quả:
Trước: Hello
Sau: Hello!!!
Giải thích:
&mut message→ Mượn mutable reference- Function có thể thay đổi giá trị
- Vẫn giữ ownership ở
main
🆚 Pass By Value vs Reference
Ví Dụ So Sánh
fn move_ownership(s: String) {
println!("Move: {}", s);
// s bị drop ở đây
}
fn borrow_immutable(s: &String) {
println!("Borrow: {}", s);
}
fn borrow_mutable(s: &mut String) {
s.push_str(" - Modified");
}
fn main() {
// 1. Move ownership
let s1 = String::from("Test 1");
move_ownership(s1);
// println!("{}", s1); // ❌ Lỗi - s1 đã move!
// 2. Borrow immutable
let s2 = String::from("Test 2");
borrow_immutable(&s2);
println!("Vẫn dùng: {}", s2); // ✅ OK
// 3. Borrow mutable
let mut s3 = String::from("Test 3");
borrow_mutable(&mut s3);
println!("Đã sửa: {}", s3); // ✅ OK
}
Kết quả:
Move: Test 1
Borrow: Test 2
Vẫn dùng: Test 2
Đã sửa: Test 3 - Modified
Khi Nào Dùng Cái Nào?
| Loại | Khi Nào Dùng | Ưu Điểm | Nhược Điểm |
|---|---|---|---|
Value T | Copy types (i32, bool) | Đơn giản | Copy overhead với lớn |
&T | Chỉ đọc, không sửa | Hiệu quả, an toàn | Không sửa được |
&mut T | Cần sửa đổi | Hiệu quả, linh hoạt | Phức tạp hơn |
// ✅ i32 → Pass by value
fn cong(a: i32, b: i32) -> i32 { a + b }
// ✅ String → Pass by reference (đọc)
fn in_string(s: &String) { println!("{}", s); }
// ✅ String → Pass by mut reference (sửa)
fn them_text(s: &mut String) { s.push_str("!"); }
// ❌ Tránh - Move ownership trừ khi cần thiết
fn xu_ly(s: String) { /* s bị drop */ }
🎮 Ví Dụ Thực Tế: Xử Lý Vector
fn tinh_tong(numbers: &Vec<i32>) -> i32 {
let mut sum = 0;
for &num in numbers {
sum += num;
}
sum
}
fn nhan_doi_tat_ca(numbers: &mut Vec<i32>) {
for num in numbers.iter_mut() {
*num *= 2;
}
}
fn main() {
let mut data = vec![10, 20, 30, 40];
println!("Dữ liệu: {:?}", data);
let tong = tinh_tong(&data); // Mượn immutable
println!("Tổng: {}", tong);
nhan_doi_tat_ca(&mut data); // Mượn mutable
println!("Sau nhân đôi: {:?}", data);
}
Kết quả:
Dữ liệu: [10, 20, 30, 40]
Tổng: 100
Sau nhân đôi: [20, 40, 60, 80]
🔄 Return Ownership
Function có thể trả về ownership:
fn tao_string(text: &str) -> String {
String::from(text) // ✅ Trả về ownership
}
fn them_huyen(mut s: String) -> String {
s.push('!');
s // ✅ Trả về ownership
}
fn main() {
let s1 = tao_string("Hello");
println!("s1: {}", s1);
let s2 = them_huyen(s1); // s1 move vào function
// println!("{}", s1); // ❌ Lỗi - s1 đã move
println!("s2: {}", s2);
}
Kết quả:
s1: Hello
s2: Hello!
🎯 Pattern: Mượn, Xử Lý, Trả Về
Pattern phổ biến: Mượn đầu vào, tạo mới, trả về:
fn capitalize(s: &str) -> String {
s.to_uppercase() // Tạo String mới
}
fn main() {
let text = "rust";
let upper = capitalize(text);
println!("Gốc: {}", text);
println!("Viết hoa: {}", upper);
}
Kết quả:
Gốc: rust
Viết hoa: RUST
📊 Ví Dụ: Tìm Kiếm Trong Array
fn tim_kiem(arr: &[i32], target: i32) -> Option<usize> {
for (index, &value) in arr.iter().enumerate() {
if value == target {
return Some(index);
}
}
None
}
fn main() {
let numbers = [10, 25, 30, 42, 55];
match tim_kiem(&numbers, 30) {
Some(index) => println!("Tìm thấy tại vị trí {}", index),
None => println!("Không tìm thấy"),
}
match tim_kiem(&numbers, 99) {
Some(index) => println!("Tìm thấy tại vị trí {}", index),
None => println!("Không tìm thấy"),
}
}
Kết quả:
Tìm thấy tại vị trí 2
Không tìm thấy
⚠️ Lỗi Thường Gặp
1. Quên & Khi Truyền Reference
fn xu_ly(s: &String) {
println!("{}", s);
}
fn main() {
let text = String::from("Hello");
// ❌ Lỗi - Thiếu &
// xu_ly(text);
// ✅ Đúng
xu_ly(&text);
}
2. Sử Dụng Sau Khi Move
fn consume(s: String) {
println!("{}", s);
}
fn main() {
let text = String::from("Hello");
consume(text); // text bị move
// ❌ Lỗi - text đã move
// println!("{}", text);
}
Sửa: Dùng reference:
fn consume(s: &String) {
println!("{}", s);
}
fn main() {
let text = String::from("Hello");
consume(&text);
println!("{}", text); // ✅ OK
}
3. Nhiều Mutable Borrows
fn main() {
let mut data = vec![1, 2, 3];
let r1 = &mut data;
// let r2 = &mut data; // ❌ Lỗi - Chỉ một &mut tại một thời điểm!
r1.push(4);
}