Bỏ qua để đến nội dung

Hàm trong Rust

Hàm (function) là một khối code có tên và dùng lại được. Thay vì viết đi viết lại cùng những bước giống nhau, bạn đặt chúng vào một hàm một lần rồi gọi hàm đó bất cứ khi nào cần. Thật ra bạn đã dùng một hàm rồi: main.

Bạn định nghĩa hàm bằng fn, và gọi nó bằng cách viết tên hàm theo sau là ():

fn greet() {
println!("Xin chào từ một hàm!");
}
fn main() {
greet(); // gọi hàm
greet(); // gọi lần nữa
}

Kết quả:

Xin chào từ một hàm!
Xin chào từ một hàm!
▶ Tự chạy thử

Hàm trở nên hữu ích khi bạn truyền cho nó dữ liệu để xử lý. Những dữ liệu đầu vào này gọi là tham số (parameter). Trong Rust bạn bắt buộc phải ghi kiểu của từng tham số:

fn greet(name: &str) {
println!("Xin chào, {name}!");
}
fn main() {
greet("Alice");
greet("Bob");
}

Kết quả:

Xin chào, Alice!
Xin chào, Bob!
▶ Tự chạy thử

Bạn có thể có bao nhiêu tham số tùy ý, cách nhau bằng dấu phẩy:

fn describe(name: &str, age: u32) {
println!("{name} năm nay {age} tuổi");
}
fn main() {
describe("Ferris", 10);
}

Kết quả:

Ferris năm nay 10 tuổi
▶ Tự chạy thử

Các giá trị bạn truyền vào được ghép với tham số theo vị trí: giá trị thứ nhất vào name, giá trị thứ hai vào age. Truyền sai thứ tự, như describe(10, "Ferris"), là lỗi biên dịch, vì các kiểu không khớp.

Một hàm có thể trả một giá trị về cho nơi đã gọi nó. Bạn khai báo kiểu trả về sau mũi tên ->:

fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let sum = add(3, 4);
println!("3 + 4 = {sum}");
}

Kết quả:

3 + 4 = 7
▶ Tự chạy thử

Để ý rằng a + b trong ví dụ trên không có dấu chấm phẩy. Đây là một ý tưởng then chốt của Rust:

  • Một biểu thức (expression) không có dấu chấm phẩy là giá trị được trả về.
  • Thêm dấu chấm phẩy sẽ biến nó thành một câu lệnh (statement) không trả về gì cả.

Vì thế đoạn này bị lỗi:

fn add(a: i32, b: i32) -> i32 {
a + b; // ❌ dấu chấm phẩy nghĩa là "không trả về gì" — nhưng ta đã hứa trả về i32
}

Trình biên dịch sẽ báo rằng hàm lẽ ra phải trả về một i32 nhưng lại không trả. Bạn chỉ cần bỏ dấu chấm phẩy ở dòng cuối.

fn square(n: i32) -> i32 {
n * n
}
fn main() {
let result = square(5);
println!("5 bình phương là {result}");
}

Kết quả:

5 bình phương là 25
▶ Tự chạy thử

Viết hàm to_fahrenheit nhận nhiệt độ theo độ C dưới dạng f64 và trả về nhiệt độ theo độ F. Công thức là c * 9.0 / 5.0 + 32.0. Gọi nó với 0, 100 và 37 độ. Gợi ý: chữ ký hàm là fn to_fahrenheit(c: f64) -> f64, và thân hàm là một biểu thức duy nhất không có dấu chấm phẩy.

Xem lời giải
fn to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn main() {
println!("0°C là {}°F", to_fahrenheit(0.0));
println!("100°C là {}°F", to_fahrenheit(100.0));
println!("37°C là {}°F", to_fahrenheit(37.0));
}

Kết quả:

0°C là 32°F
100°C là 212°F
37°C là 98.6°F
▶ Tự chạy thử

Để ý rằng ta truyền 0.0 chứ không phải 0. Tham số là f64, và Rust sẽ không âm thầm đổi số nguyên thành số thực giúp bạn. Khi một f64 không có phần thập phân, {} in nó ra mà không có .0, vì thế bạn thấy 32 chứ không phải 32.0.

Viết hàm largest(a: i32, b: i32, c: i32) -> i32 trả về số lớn nhất trong ba số. Thử với (3, 9, 4) và (-2, -8, -5). Gợi ý: if là một biểu thức trong Rust (xem bài If…Else), nên một chuỗi if / else if / else có thể làm dòng cuối của hàm.

Xem lời giải
fn largest(a: i32, b: i32, c: i32) -> i32 {
if a >= b && a >= c {
a
} else if b >= c {
b
} else {
c
}
}
fn main() {
println!("{}", largest(3, 9, 4));
println!("{}", largest(-2, -8, -5));
}

Kết quả:

9
-2
▶ Tự chạy thử

Không nhánh nào kết thúc bằng dấu chấm phẩy. Mỗi nhánh là một biểu thức, cả chuỗi if trả về một trong số chúng, và đó trở thành giá trị trả về. Dùng >= thay vì > đảm bảo khi có hai số bằng nhau, hàm vẫn trả về một giá trị.

  • Định nghĩa hàm bằng fn, gọi hàm bằng name().
  • Tham số là đầu vào; mỗi tham số cần có kiểu: fn greet(name: &str).
  • Khai báo kiểu trả về bằng -> Type.
  • Biểu thức cuối cùng không có dấu chấm phẩy là giá trị trả về.
  • return value; cũng được, nhưng thường chỉ dùng khi muốn trả về sớm.

Kiểm tra nhanh

1. Trong Rust, bạn có cần ghi kiểu cho từng tham số của hàm không?

2. Khai báo kiểu trả về của hàm như thế nào?

3. Điều gì khiến a + b (không có dấu chấm phẩy) ở cuối hàm trở thành giá trị trả về?

4. Định nghĩa hàm ở trên hay ở dưới chỗ gọi nó (ví dụ trên hay dưới main) có quan trọng không?

Điểm: 0 / 4