Skip to content

Functional Features in Rust Idiomatic

Rust chịu ảnh hưởng mạnh từ Functional Programming (Haskell, OCaml)

Hai tính năng chính của FP trong Rust là:

  1. Closures: Anonymous functions (hàm ẩn danh) có thể lưu vào biến.
  2. Iterators: Xử lý chuỗi items, lazy evaluation và nhanh ngang code thủ công.

1. Closures: Environment Capture

Closure giống function nhưng có thể capture (nắm bắt/nhìn thấy) các biến trong scope nơi nó được định nghĩa.

Cú pháp

Định nghĩa bằng |args| body.

rust
fn main() {
    let x = 4;

    // Closure capture 'x' từ môi trường
    let equal_to_x = |z| z == x;

    let y = 4;
    assert!(equal_to_x(y));
}

Type Inference

Khác với fn, closure không bắt buộc khai báo type đầu vào/ra (compiler tự đoán).

rust
let example_closure = |x| x;
let n = example_closure(5); 
// let s = example_closure(String::from("hello")); 
// ❌ ERROR: Compiler đã lock type là integer ở lần gọi đầu tiên

Capture Modes: Fn, FnMut, FnOnce

Closure capture biến theo 3 traits (tương ứng với Ownership rules):

  1. FnOnce: Takes ownership (Move). Chỉ gọi được 1 lần.
  2. FnMut: Borrows mutably. Có thể sửa đổi captured values.
  3. Fn: Borrows immutably. Chỉ đọc.
rust
// FnMut Example
let mut list = vec![1, 2, 3];
let mut borrows_mutably = || list.push(7);

borrows_mutably();
println!("{:?}", list);

move keyword: Bắt buộc closure lấy ownership (hữu ích khi dùng thread).

rust
let list = vec![1, 2, 3];
let owns_list = move || println!("{:?}", list);
// list đã bị move vào closure, không dùng được ở ngoài nữa

2. Iterators: Processing Series of Items

Iterator pattern cho phép thực hiện task trên chuỗi items lần lượt.

Lazy Evaluation

Iterator trong Rust là Lazy. Nó không làm gì cho đến khi bạn gọi method "consume" nó.

rust
let v1 = vec![1, 2, 3];

// Chưa làm gì cả, chỉ tạo iterator object
let v1_iter = v1.iter();

// Giờ mới chạy
for val in v1_iter {
    println!("Got: {}", val);
}

Iterator Adaptors (map, filter)

Các hàm biến đổi iterator thành iterator khác. Vẫn là Lazy.

rust
let v1: Vec<i32> = vec![1, 2, 3];

// Map + 1
// collect() là hàm "consume", kích hoạt chain chạy và gom kết quả về Vec
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();

assert_eq!(v2, vec![2, 3, 4]);
rust
// Filter lấy số lẻ
let v3: Vec<_> = v1.into_iter().filter(|x| x % 2 != 0).collect();

Iterator Consumers (sum, fold)

Các hàm "ăn" iterator và trả về giá trị cuối cùng.

rust
let v1 = vec![1, 2, 3];
let total: i32 = v1.iter().sum(); // 6

3. Performance: Loop vs Iterator

Bạn có thể lo lắng Iterator chậm hơn Loop for thủ công?

SỰ THẬT: Iterator trong Rust thường NHANH HƠN hoặc BẰNG Loop.

Đây là Zero-cost Abstraction. Compiler (LLVM) optimize iterators rất tốt (unrolling, vectorization).

💡 BENCHMARK NOTE

Đừng ngại dùng Iterator. Nó ngắn gọn, an toàn (không bounds check lỗi) và cực nhanh. Cha đẻ C++ (Bjarne Stroustrup) cũng ước C++ ranges được như này.


4. Ví dụ thực tế: Cải tiến code với FP

Code ban đầu (Imperative)

rust
fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new(); // Mutable state
    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }
    results
}

Code cải tiến (Idiomatic FP)

rust
fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

Ngắn hơn, rõ ý hơn, không cần mut, và performance tương đương.


Bảng Tóm tắt Iterators

MethodLoạiMô tả
iter()ConstructorTạo iterator các references &T.
into_iter()ConstructorTạo iterator các owned values T (consume collection).
iter_mut()ConstructorTạo iterator các mutable references &mut T.
map(f)AdaptorBiến đổi từng phần tử.
filter(f)AdaptorGiữ lại phần tử thỏa điều kiện.
collect()ConsumerBiến iterator thành collection (Vec, String...).