Trong phần này chúng ta sẽ tìm hiểu về kiểu hàm higher order.
Thuật ngữ hàm higher order có nghĩa là một hàm có thể nhận tham số là một hàm khác, vì hàm thực chất cũng là các đối tượng nên cũng có thể truyền vào làm tham số được, khái niệm này cũng gần giống với khái niệm hàm callback vậy, chỉ khác ở chỗ là hàm higher order được gọi một cách tự do hơn, trong bài này mình sẽ không đi phân tích sự khác nhau giữa higher order và callback.
Cú pháp để định nghĩa một hàm higher order như sau:
fn tên_hàm<tên kiểu : Fn(danh_sách_tham_số)>(danh_sách_tham_số) {
}
Ví dụ:
fn main() {
higherOrder(childFunction);
}
fn higherOrder<Ham: Fn()>(f: Ham) {
println!("This is the higher-order function calling to the child function");
f();
}
fn childFunction() {
println!("This is the child function");
}
Trong đoạn code trên, chúng ta định nghĩa một hàm higher order có tên là higherOrder()
, hàm này nhận vào một tham số là một hàm khác có kiểu là Ham
và chúng ta đặt tên tham số là f
. Chúng ta định nghĩa tên và tham số của kiểu Ham
ngay phía trước cặp dấu ngoặc tròn ()
.
Về cú pháp định nghĩa kiểu Ham
thì chúng ta định nghĩa bên trong cặp dấu <>
, đầu tiên là tên của kiểu dữ liệu hàm, rồi tới từ khóa Fn
và cặp dấu ngoặc tròn, bên trong chúng ta cũng có thể khai báo các tham số, ở đoạn code trên chúng ta không có tham số nào.
Khi gọi hàm higher order thì chúng ta truyền tham số là tên của hàm khác, nếu hàm nhận vào có tham số thì chúng ta cũng mở dấu ngoặc và đưa tham số vào như bình thường, lưu ý là nếu hàm con không nhận tham số (như trong ví dụ trên) thì chúng ta không đưa cặp dấu ngoặc tròn vào.
This is the higher-order function calling to the child function This is the child function
Truyền hàm closure – hay hàm anonymous
Chúng ta cũng có thể truyền các hàm closure thay vì định nghĩa một hàm một cách bình thường, ví dụ:
fn main() {
let square = |number| {
return (number * number);
};
higherOrder(square, 12);
}
fn higherOrder<HamSquare: Fn(i32) -> i32>(f: HamSquare, n: i32) {
println!("{}^{} = {}", n, n, f(n));
}
Trong đoạn code trên chúng ta định nghĩa hàm con có tên là square
ngay bên trong hàm main()
, và do đó square
được gọi là hàm closure, hàm này nhận vào một số nguyên và trả về bình phương của số đó.
Sau đó bên trong hàm higherOrder()
, chúng ta định nghĩa kiểu HamSquare
nhận vào một số nguyên và trả về một số nguyên, mục đích là để giống với cú pháp của hàm square
, ngoài ra chúng ta còn cho nhận thêm một số nguyên nữa để có thể truyền vào hàm con này.
12^12 = 144
Ngoài ra Rust cũng cho phép chúng ta truyền phần định nghĩa hàm closure vào bên trong lời gọi hàm luôn, ví dụ:
fn main() {
higherOrder(|number| {
return (number * number);
}, 12);
}
fn higherOrder<HamSquare: Fn(i32) -> i32>(f: HamSquare, n: i32) {
println!("{}^{} = {}", n, n, f(n));
}
Đoạn code trên vẫn cho ra kết quả cũ, chỉ khác là chúng ta truyền phần định nghĩa hàm vào trong lời gọi hàm luôn, tức là không còn tồn tại cái tên square nữa.