Menu
CoddyTech

Trait có tham số

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 33 trên 61.

Cho đến nay, các phương thức của trait chỉ nhận &self làm tham số. Nhưng phương thức của trait có thể nhận thêm tham số, giống như phương thức thông thường. Điều này giúp chúng hữu ích hơn nhiều trong các thao tác thực tế.

Khi định nghĩa một trait, bạn có thể thêm mọi tham số cần thiết vào chữ ký phương thức:

trait Calculator {
    fn add(&self, a: i32, b: i32) -> i32;
    fn multiply(&self, a: i32, b: i32) -> i32;
}

Trait Calculator này yêu cầu hai phương thức, mỗi phương thức nhận hai số nguyên ngoài &self. Mọi kiểu triển khai trait này đều phải cung cấp cả hai phép toán với chữ ký chính xác như sau.

Sau đây là cách một struct có thể triển khai nó:

struct BasicCalc;

impl Calculator for BasicCalc {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    
    fn multiply(&self, a: i32, b: i32) -> i32 {
        a * b
    }
}

Giờ đây, bạn có thể dùng các phương thức này với đối số:

let calc = BasicCalc;
println!("{}", calc.add(5, 3));       // 8
println!("{}", calc.multiply(4, 7));  // 28

Trait quy định mỗi phương thức nhận những tham số nào, còn phần triển khai quy định cách dùng những tham số đó ra sao. Nhờ vậy, các kiểu dữ liệu khác nhau có thể thực hiện cùng một thao tác theo cách riêng: chẳng hạn, LoggingCalc có thể in ra từng phép tính trước khi trả về kết quả.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một bộ công cụ thao tác chuỗi, trong đó các bộ xử lý khác nhau có thể biến đổi văn bản theo cách riêng! Bạn sẽ tạo một trait TextProcessor với các phương thức nhận tham số, rồi triển khai trait này cho hai kiểu bộ xử lý khác nhau.

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

  • processor.rs: Định nghĩa trait công khai TextProcessor với hai phương thức nhận tham số:
    • repeat(&self, text: &str, times: u32) -> String: lặp lại văn bản đã cho một số lần xác định
    • truncate(&self, text: &str, max_len: usize) -> String: rút ngắn văn bản đến độ dài tối đa đã xác định
  • processors.rs: Tạo hai struct công khai triển khai trait của bạn:
    • SimpleProcessor: một struct đơn vị, lặp lại văn bản với dấu cách giữa mỗi lần lặp (ví dụ: "Hi" lặp lại 3 lần thành "Hi Hi Hi") và cắt văn bản tại độ dài tối đa
    • FancyProcessor: một struct đơn vị, lặp lại văn bản với " * " giữa mỗi lần lặp (ví dụ: "Hi" lặp lại 3 lần thành "Hi * Hi * Hi") và thêm "..." nếu văn bản bị rút ngắn (chỉ khi văn bản ban đầu dài hơn max_len)
  • main.rs: Kết nối các module và minh họa cách những phương thức của cùng một trait tạo ra kết quả khác nhau tùy vào bộ xử lý bạn dùng.

Trong tệp chính, hãy tạo cả hai bộ xử lý và dùng các đầu vào để kiểm tra chúng. Bạn sẽ nhận ba đầu vào: một chuỗi văn bản, số lần lặp và độ dài tối đa.

In ra bốn dòng thể hiện cách mỗi bộ xử lý hoạt động:

Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}

Ví dụ, với các đầu vào Hello, 3 và 4:

Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...

Hãy chú ý cách cả hai bộ xử lý đều tuân theo cùng một hợp đồng TextProcessor, nhưng mỗi bộ lại biến đổi văn bản theo cách riêng. Trait định nghĩa các tham số mà phương thức nhận, còn mỗi phần triển khai quyết định cách sử dụng chúng!

Bạn sẽ nhận ba đầu vào: văn bản cần xử lý, số lần lặp (phân tích thành u32) và độ dài tối đa để cắt ngắn (phân tích thành usize).

Tự làm thử

mod processor;
mod processors;

use processor::TextProcessor;
use processors::{SimpleProcessor, FancyProcessor};

fn main() {
    // Đọc đầu vào
    let mut text = String::new();
    std::io::stdin().read_line(&mut text).expect("Failed to read line");
    let text = text.trim();
    
    let mut times_input = String::new();
    std::io::stdin().read_line(&mut times_input).expect("Failed to read line");
    let times: u32 = times_input.trim().parse().expect("Failed to parse times");
    
    let mut max_len_input = String::new();
    std::io::stdin().read_line(&mut max_len_input).expect("Failed to read line");
    let max_len: usize = max_len_input.trim().parse().expect("Failed to parse max_len");
    
    // Tạo các bộ xử lý
    let simple = SimpleProcessor;
    let fancy = FancyProcessor;
    
    // TODO: Dùng các bộ xử lý để biến đổi văn bản và in kết quả
    // In ra bốn dòng:
    // Simple repeat: {result}
    // Simple truncate: {result}
    // Fancy repeat: {result}
    // Fancy truncate: {result}
}
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