The Problem

Một trait được khai báo với tính chất generic đối với kiểu container của nó có các yêu cầu về kiểu - người sử dụng trait phải chỉ định tất cả các kiểu generic của nó.

Trong ví dụ dưới đây, trait Contains cho phép sử dụng các kiểu generic AB. Sau đó, trait được triển khai cho kiểu Container, chỉ định kiểu i32 cho AB để có thể sử dụng với hàm difference().

Bởi vì Contains là generic, chúng ta buộc phải chỉ định rõ ràng tất cả các kiểu generic cho fn difference(). Trong thực tế, chúng ta muốn có một cách để biểu thị rằng AB được xác định bởi đầu vào C. Bạn sẽ thấy trong phần tiếp theo, associated types cho ta khả năng đó.

struct Container(i32, i32);

// Trait kiểm tra xem 2 phần tử có được lưu trữ bên trong container hay không.
// Trait cũng trích xuất giá trị đầu tiên hoặc cuối cùng.
trait Contains<A, B> {
    fn contains(&self, _: &A, _: &B) -> bool; // Yêu cầu `A` và `B` rõ ràng.
    fn first(&self) -> i32; // Không yêu cầu rõ ràng `A` hoặc `B`.
    fn last(&self) -> i32;  // Không yêu cầu rõ ràng `A` hoặc `B`.
}

impl Contains<i32, i32> for Container {
    // Trả về `true` nếu các số được lưu trữ bằng nhau.
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }

    // Lấy số đầu tiên.
    fn first(&self) -> i32 { self.0 }

    // Lấy số cuối cùng.
    fn last(&self) -> i32 { self.1 }
}

// `C` chứa `A` và `B`. Vì vậy, phải biểu thị lại `A` và
// `B` sẽ khá là bất tiện.
fn difference<A, B, C>(container: &C) -> i32 where
    C: Contains<A, B> {
    container.last() - container.first()
}

fn main() {
    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!("Container có chứa {} và {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("Số đầu: {}", container.first());
    println!("Số cuối: {}", container.last());

    println!("Hiệu 2 số là: {}", difference(&container));
}

Xem thêm:

structs, và traits