Menu
CoddyTech

Hàm tổng quát

Thuộc phần Lập trình hướng đối tượng trong hành trình Rust của Coddy. Bài học 27 trên 61.

Generics không chỉ giới hạn ở các struct. Bạn cũng có thể viết các hàm độc lập hoạt động với mọi kiểu dữ liệu. Cách này hữu ích khi bạn cần một hàm tiện ích không thuộc về một struct cụ thể nhưng vẫn cần linh hoạt.

Cú pháp tương tự như những gì bạn đã thấy với struct. Khai báo tham số generic trong dấu ngoặc nhọn góc sau tên hàm, rồi dùng nó trong các tham số và kiểu trả về:

fn identity<T>(value: T) -> T {
    value
}

Hàm identity này nhận một giá trị thuộc bất kỳ kiểu dữ liệu nào và trả về giá trị đó không thay đổi. <T> khai báo kiểu tổng quát, value: T nhận giá trị đó làm tham số, và -> T chỉ định kiểu trả về. Khi bạn gọi hàm, Rust tự suy luận kiểu cụ thể:

let num = identity(42);        // T là i32
let text = identity("hello");  // T là &str

Bạn cũng có thể dùng nhiều tham số tổng quát trong các hàm, giống như với struct:

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // trả về (i32, &str)

Các hàm tổng quát (generic function) giúp bạn viết logic có thể tái sử dụng một lần rồi áp dụng cho nhiều kiểu dữ liệu, giảm trùng lặp code mà vẫn đảm bảo an toàn kiểu.

challenge icon

Thử thách

Dễ

Hãy tạo một module tiện ích với các hàm tổng quát có thể hoạt động với mọi kiểu dữ liệu! Bạn sẽ tạo các hàm độc lập để minh họa cách generics giúp code linh hoạt và có thể tái sử dụng mà không bị ràng buộc với một struct cụ thể.

Bạn sẽ sắp xếp code trong hai tệp:

  • utils.rs: Tạo một tập hợp các hàm tiện ích tổng quát công khai:
    • wrap_in_pair<T>: nhận một giá trị và trả về một tuple chứa giá trị đó hai lần: (value, value). Kiểu dữ liệu cần triển khai Clone, vì vậy hãy dùng <T: Clone>
    • swap<T, U>: nhận hai giá trị có thể thuộc các kiểu dữ liệu khác nhau và trả về chúng theo thứ tự đảo ngược trong một tuple (U, T)
  • main.rs: Nhập module tiện ích và minh họa cách các hàm tổng quát này hoạt động với nhiều kiểu dữ liệu. Cho thấy cùng một định nghĩa hàm có thể xử lý số nguyên, số thực và chuỗi một cách dễ dàng.

Trong tệp chính, hãy minh họa các hàm tiện ích bằng cách:

  1. Dùng wrap_in_pair với một số nguyên (đầu vào thứ nhất, được phân tích thành i32) và in ra cả hai phần tử
  2. Dùng wrap_in_pair với một chuỗi (đầu vào thứ hai) và in ra cả hai phần tử
  3. Dùng swap với một số nguyên (đầu vào thứ ba, được phân tích thành i32) và một chuỗi (đầu vào thứ tư), sau đó in ra kết quả đã hoán đổi

Đầu ra của bạn cần theo định dạng này:

Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})

Ví dụ, với các đầu vào 5, hello, 42 và world:

Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)

Hãy chú ý cách wrap_in_pair hoạt động giống hệt nhau với cả số nguyên và chuỗi, còn swap xử lý hai kiểu dữ liệu hoàn toàn khác nhau. Đó chính là sự linh hoạt của các hàm tổng quát!

Bạn sẽ nhận bốn đầu vào: một số nguyên, một chuỗi, một số nguyên khác và một chuỗi khác.

Tự làm thử

mod utils;

use std::io;

fn main() {
    // Đọc bốn đầu vào
    let mut input1 = String::new();
    io::stdin().read_line(&mut input1).expect("Failed to read line");
    let num1: i32 = input1.trim().parse().expect("Invalid integer");

    let mut input2 = String::new();
    io::stdin().read_line(&mut input2).expect("Failed to read line");
    let str1 = input2.trim().to_string();

    let mut input3 = String::new();
    io::stdin().read_line(&mut input3).expect("Failed to read line");
    let num2: i32 = input3.trim().parse().expect("Invalid integer");

    let mut input4 = String::new();
    io::stdin().read_line(&mut input4).expect("Failed to read line");
    let str2 = input4.trim().to_string();

    // TODO: Dùng wrap_in_pair với num1 rồi in kết quả
    // Định dạng: Pair of ints: ({value}, {value})

    // TODO: Dùng wrap_in_pair với str1 rồi in kết quả
    // Định dạng: Pair of strings: ({value}, {value})

    // TODO: Dùng swap với num2 và str2, rồi in kết quả sau khi hoán đổi
    // Định dạng: Swapped: ({string}, {int})
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Rust online