特征

定义trait

定义特征

抽象某类行为

例如 文章和短视频 我们都可以总结其内容

use std::fmt::{Display, Formatter};
use std::ops::{Add};

fn main() {
    trait Summary {
        // 总结文章内容
        fn summarize(&self) -> String;

        fn owner(&self) -> &str;

        // 关注作者
        fn follow(&self)  {
            println!("成功关注了 {}", self.owner())
        }
    }

    #[derive(Debug)]
    struct Article {
        author: String,
        title: String,
        content: String
    }
    struct Vlog {
        up: String,
        title: String
    }

    impl Summary for Article {
        fn summarize(&self) -> String {
            format!("{}发布了文章《{}》:\"{}\"", self.author, self.title, self.content)
        }
        fn owner(&self) -> &str {
            &(self.author)
        }
    }

    impl Summary for Vlog {
        fn summarize(&self) -> String {
            format!("{}发布了新作品<{}>,快去看看吧~", self.up, self.title)
        }

        fn owner(&self) -> &str {
            &(self.up)
        }

        // 重写
        fn follow(&self) {
            println!("您已经关注过 {} 了", self.owner())
        }
    }

    let rust_hi = Article {
        author: "哆啦B梦".to_string(),
        title: "你好,Rust".to_string(),
        content: "rust 有点难".to_string()
    };

    let manba = Vlog {
        up: "不吃香菜".to_string(),
        title: "爱吃肘击".to_string()
    };

    println!("{}", rust_hi.summarize());
    println!("{}", manba.summarize());
    rust_hi.follow();
    manba.follow();


    // 使用特征作为函数的参数

    // 只要实现了该特征的任何结构体都能作为该函数的参数
    fn broadcast(message: &impl Summary) {
        println!("震惊:{}", message.summarize());
    }

    broadcast(&rust_hi);
    broadcast(&manba);

    // 作为泛型
    fn notify<T: Summary>(a: &T, b: &T) {
        println!("你关注的 {} 和 {} 发布了新作品", a.owner(), b.owner())
    }

    let bang = Article {
        author: "GGBond".to_string(),
        title: "超级棒棒糖".to_string(),
        content: "很好吃".to_string()
    };
    notify(&rust_hi, &bang);
    // 但是
    // notify(&rust_hi, &manba); // 会报错
    // 这是因为:在notify参数中, a 和 b 必须是完全相同的类型 T。
    // 虽然 Article 和 Vlog 都实现了 Summary trait,但它们是两个不同的类型。

    // 多重约束
    fn show<T: Clone + std::fmt::Debug + std::fmt::Pointer>(data: T) {
        let new_data = data.clone(); // 这里其实克隆的是引用
        println!("{:?}", new_data);
        // 打印内存地址
        println!("原始数据指针: {:p}", data);
        println!("克隆后指针: {:p}", new_data);
        // 所以我们克隆的只是引用
    }

    println!("rust_hi数据指针: {:p}", &rust_hi);
    show(&rust_hi);

    println!("{:?}", rust_hi);

    // 使用where改写多重约束
    fn show_v2<T>(data: T)
    where
        T: Clone + std::fmt::Debug
    {
        let new_data = data.clone();
        println!("v2_: {:?}", new_data);
    }

    show_v2(&rust_hi);

    // 函数返回特征

    fn get_summary() -> impl Summary + std::fmt::Debug {
     /*   if is_vlog {
            return Vlog {
                up: "光头强".to_string(),
                title: "可恶的小熊熊".to_string()
            }
        }*/
        // 虽然 Vlog 和 Article 都实现了Summary特征
        // 但它们任然是两个类型 函数任然不能动态的返回两个类型
        // Rust 编译器需要在编译时就知道确切的返回类型

        Article {
            author: "光头强".to_string(),
            title: "可恶".to_string(),
            content: "可恶的小熊熊".to_string()
        }
    }

    println!("{:?}", get_summary());
}

有条件的实现trait

use std::fmt::{Display, Formatter};
use std::ops::{Add};

fn main() {
    #[derive(Debug)]
    struct Storage<T> {
        x: T,
        y: T
    }

    impl <T>Storage<T> {
        fn new(x: T, y: T) -> Self {
            Storage {
                x,
                y
            }
        }
    }

    impl <T: Add<Output = T> + Copy> Storage<T> {
        fn sum_x_and_y(&self) -> T {
            self.x + self.y
        }
    }

    impl <T: Copy + ToString>Storage<T> {
        fn put_x_and_y(&self) -> String {
            self.x.to_string() + &(self.y.to_string())
        }
    }

    impl Storage<bool> {
        fn every(&self) -> bool {
            self.x && self.y
        }
    }

    let s1 = Storage::new(2, 1);
    println!("{}", s1.sum_x_and_y());
    let s2 = Storage::new("熊大", "熊二");
    println!("{}", s2.put_x_and_y());
    // println!("{}", s2.sum_x_and_y()); // 会报错 因为s2没有实现 Add trait
    // println!("{}", s2.every()); // 会报错  因为s2不属于 bool类型
    let s3 = Storage::new(true, true);
    println!("{}", s3.every());
    println!("{}", s3.put_x_and_y()); // bool 可以 toString

    // 为自定义结构体实现加法运算
    impl <T: Add<Output = T>>Add for Storage<T> {
        type Output = Storage<T>;

        fn add(self, rhs: Storage<T>) -> Storage<T> {
            Storage {
                x: self.x + rhs.x,
                y: self.y + rhs.y
            }
        }
    }

    let s4 = Storage::new(1, 2);
    let s5 = Storage::new(1, 2);
    let s6 = s4 + s5;
    println!("{:?}", s6);
    // 这里就无法再访问s4 s5了 因为他们在做加法运算时 所有权被移走了
    // println!("{}", s4);
    // println!("{}", s5);

    // 为自定义结构体实现Display

    impl <T: Display> Display for Storage<T> {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            write!(f, "<x: {}, y: {}>", self.x, self.y)
        }
    }

    let s7 = Storage::new("哈哈", "大笑");
    println!("{:?}", s7);
    println!("{}", s7); // 因为Storage实现了Display特征 所以我们输出时可以不需要:?
}