Rust中的特型Trait类似于Java中的接口Interface或抽象类Abstract Class,有些不同的是,Trait可以用于任意结构,包括系统内置结构体或自定义的结构体。

要求:创建一个泛型数据容器 GenericContainer,并定义一个自定义 Processable Trait。

  1. Processable Trait:
    • 定义一个名为 process(&self) -> String 的 Trait,要求实现者返回一个表示其处理结果的字符串。
    • 为 i32 实现 Processable Trait(例如,返回 “Processed integer: N”)。
    • 为 String 实现 Processable Trait(例如,返回 “Processed string: ‘S’“)。
    • 定义一个简单的 Point 结构体(包含 x: i32, y: i32),并为其实现 Processable Trait(例如,返回 “Processed point: (X, Y)”)。
  2. GenericContainer 结构体:
    • 应包含一个 Vec 来存储数据。
    • 实现一个 new() 关联函数来创建实例。
    • 实现一个 add_item(item: T) 方法来向容器中添加元素。
    • 实现一个 process_all_items(&self) -> Vec 方法。这个方法应遍历容器中的所有元素,并为每个元素调用其 process() 方法,然后将所有结果字符串收集到一个 Vec 中返回。注意: 只有当 T 类型实现了 Processable Trait 时,才能调用此方法。
  3. 测试:
    • 创建 GenericContainer 实例,添加一些整数,然后处理并打印结果。
    • 创建 GenericContainer 实例,添加一些字符串,然后处理并打印结果。
    • 创建 GenericContainer 实例,添加一些点,然后处理并打印结果。

下面是例子代码,需要注意的是下面process_all_items方法,为了能够达到每个x都有x.process()的方法,impl的部分一定要加上impl<T: Processable>,并且范型的GenericContainer不需要重复规定T。

pub trait Processable{
    fn process(&self) -> String; 
}

impl Processable for i32 {
    fn process(&self) -> String {
        format!("Processed integer: {}", &self)
    }
}

impl Processable for String {
    fn process(&self) -> String {
        format!("Processed String: {}", &self)
    }
}
pub struct Point {
    x: i32, 
    y: i32, 
}

impl Processable for Point {
    fn process(&self) -> String {
        format!("Processed Point({}, {})", self.x, self.y)
    }
}

pub struct GenericContainer<T> {
    list: Vec<T>
}

impl<T: Processable> GenericContainer<T> {      // 注意T的类型的写法
    pub fn new() -> Self {                      
        GenericContainer { list: Vec::new() }
    }

    pub fn add_item(&mut self, item: T) {
        self.list.push(item);
    }

    pub fn process_all_items(&self) -> Vec<String> {
        self.list.iter().map(|x| x.process()).collect() 
    }
}

#[cfg(test)]
mod tests {
    use super::*; 

    #[test]
    fn test_process_point() {
        let p = Point{
            x: 100,
            y: -50, 
        };         
        assert_eq!(p.process(), "Processed Point(100, -50)");
    }
    #[test]
    fn test_process_i32() {
        let i = 100;      
        assert_eq!(i.process(), "Processed integer: 100");
    }

    #[test]
    fn test_process_string() {
        let hello = "hello,world".to_string(); 
        assert_eq!(hello.process(), "Processed String: hello,world");
    }

    #[test]
    fn test_container_i32() {
        let mut c = GenericContainer::new(); 

        for i in 0..2 {
            c.add_item(i);
        }

        let result = c.process_all_items(); 
        assert_eq!(
            format!("{:?}", result), 
            "[\"Processed integer: 0\", \"Processed integer: 1\"]"
        ); 
    }

    #[test]
    fn test_container_string() {
        let mut c = GenericContainer::new(); 
        c.add_item("hello".to_string());
        c.add_item("there".to_string());

        let result = c.process_all_items(); 
        assert_eq!(
            format!("{:?}", result), 
            "[\"Processed String: hello\", \"Processed String: there\"]"
        ); 
    }

    #[test]
    fn test_container_point() {
        let mut c = GenericContainer::new(); 
        c.add_item(Point{x:1, y:2});
        c.add_item(Point{x:0, y:-2});
        
        let result = c.process_all_items(); 
        assert_eq!(
            format!("{:?}", result), 
            "[\"Processed Point(1, 2)\", \"Processed Point(0, -2)\"]"
        ); 
    }
}