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ả.
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 khaiTextProcessorvớ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 địnhtruncate(&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 đaFancyProcessor: 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}
}
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
1Phương thức và hành vi
Nhập môn khối triển khaiTham số selfPhương thức có thể thay đổiHàm liên kếtNhiều khối triển khaiNối chuỗi phương thứcÔn tập: thao tác với hình chữ nhật4Dự án: Thú cưng ảo
Định nghĩa thú cưngCho thú cưng ăn7Các trait tiêu chuẩn
Trait DebugTrait DisplayClone và CopyCác trait so sánh bằngÔn tập: Điểm có thể in ra2Tính đóng gói và module
Kiến thức cơ bản về moduleTừ khóa publicTrường riêng tưHàm lấy giá trịHàm thiết lập giá trịÔn tập: tủ khóa an toàn5Kiểu tổng quát
Cấu trúc tổng quátPhương thức tổng quátNhiều kiểu tổng quátHàm tổng quátÔn tập: điểm tọa độTự luyện tập: Trình biên dịch Rust online