• 【Rust练习】18.特征 Trait


    练习题来自:https://practice-zh.course.rs/generics-traits/traits.html

    1

    
    // 完成两个 `impl` 语句块
    // 不要修改 `main` 中的代码
    trait Hello {
        fn say_hi(&self) -> String {
            String::from("hi")
        }
    
        fn say_something(&self) -> String;
    }
    
    struct Student {}
    impl Hello for Student {
    }
    struct Teacher {}
    impl Hello for Teacher {
    }
    
    fn main() {
        let s = Student {};
        assert_eq!(s.say_hi(), "hi");
        assert_eq!(s.say_something(), "I'm a good student");
    
        let t = Teacher {};
        assert_eq!(t.say_hi(), "Hi, I'm your new teacher");
        assert_eq!(t.say_something(), "I'm not a bad teacher");
    
        println!("Success!")
    }
    

    特征类似于 C++ 中的纯虚类(因为特征本身无法实例化)。Rust 将“继承”改为了“实现了某个特征”,让面向对象的这套体系更灵活了。但是我个人觉得这套体系有点过于复杂了,似乎是在掩盖本身的设计缺陷。

    这里将对应的方法实现即可。

    struct Student {}
    impl Hello for Student {
        fn say_something(&self) -> String{
            String::from("I'm a good student")
        }
    }
    struct Teacher {}
    impl Hello for Teacher {
        fn say_hi(&self) -> String {
            String::from("Hi, I'm your new teacher")
        }
        fn say_something(&self) -> String{
            String::from("I'm not a bad teacher")
        }
    }
    

    2

    
    // `Centimeters`, 一个元组结构体,可以被比较大小
    #[derive(PartialEq, PartialOrd)]
    struct Centimeters(f64);
    
    // `Inches`, 一个元组结构体可以被打印
    #[derive(Debug)]
    struct Inches(i32);
    
    impl Inches {
        fn to_centimeters(&self) -> Centimeters {
            let &Inches(inches) = self;
    
            Centimeters(inches as f64 * 2.54)
        }
    }
    
    // 添加一些属性让代码工作
    // 不要修改其它代码!
    struct Seconds(i32);
    
    fn main() {
        let _one_second = Seconds(1);
    
        println!("One second looks like: {:?}", _one_second);
        let _this_is_true = _one_second == _one_second;
        let _this_is_false = _one_second > _one_second;
    
        let foot = Inches(12);
    
        println!("One foot equals {:?}", foot);
    
        let meter = Centimeters(100.0);
    
        let cmp =
            if foot.to_centimeters() < meter {
                "smaller"
            } else {
                "bigger"
            };
    
        println!("One foot is {} than one meter.", cmp);
    }
    

    Centimeters需要实现Debug PartialEq PartialOrd特征,答案都写在上面了,当然你要是非得自己实现一下那我也没意见。

    // 添加一些属性让代码工作
    // 不要修改其它代码!
    #[derive(Debug)]
    #[derive(PartialEq, PartialOrd)]
    struct Seconds(i32);
    

    从 C++ 的角度看这几个问题,Debug是老大难问题了,基本只能靠类自己去实现打印函数,当然我也觉得这种东西用上继承也没有意义;另外两种运算符本身其实是重载了Operator,一般不认为是继承。例子如下:

    struct S
    {
    public:
        S(int num) : num(num) {};
        bool operator==(S s)
        {
            return this->num == s.num;
        }
    
    private:
        int num;
    };
    
    int main()
    {
        S s1{1};
        S s2{1};
        cout << (s1 == s2) << endl;
    }
    

    这里重载了==运算符,使得两个结构体之间可以比较。因为这不是继承,因此也无需显式指出S必须继承了==,但是如果S没有重载==,那IDE依然会给出警告。

    3

    
    use std::ops;
    
    // 实现 fn multiply 方法
    // 如上所述,`+` 需要 `T` 类型实现 `std::ops::Add` 特征
    // 那么, `*` 运算符需要实现什么特征呢? 你可以在这里找到答案: https://doc.rust-lang.org/core/ops/
    fn multiply
    
    fn main() {
        assert_eq!(6, multiply(2u8, 3u8));
        assert_eq!(5.0, multiply(1.0, 5.0));
    
        println!("Success!")
    }
    

    注意两点:

    1. 两个操作数是相同的类型,所以要用一般的泛型声明,而不是特征的语法糖
    2. 别忘了写返回值
    fn multiply<T: Mul<T, Output = T>>(n1: T, n2: T) -> T {
        n1 * n2
    }
    

    4

    
    // 修复错误,不要修改 `main` 中的代码!
    use std::ops;
    
    struct Foo;
    struct Bar;
    
    struct FooBar;
    
    struct BarFoo;
    
    // 下面的代码实现了自定义类型的相加: Foo + Bar = FooBar
    impl ops::Add<Bar> for Foo {
        type Output = FooBar;
    
        fn add(self, _rhs: Bar) -> FooBar {
            FooBar
        }
    }
    
    impl ops::Sub<Foo> for Bar {
        type Output = BarFoo;
    
        fn sub(self, _rhs: Foo) -> BarFoo {
            BarFoo
        }
    }
    
    fn main() {
        // 不要修改下面代码
        // 你需要为 FooBar 派生一些特征来让代码工作
        assert_eq!(Foo + Bar, FooBar);
        assert_eq!(Foo - Bar, BarFoo);
    
        println!("Success!")
    }
    

    这段代码缺少下面几个特征:

    1. FooBar之间的比较
    2. BarFoo之间的比较
    3. FooBar的减法
    4. 由于asser_eq需要debug打印,FooBarBarFoo也需要实现这个特征,但是只需要derive派生一下就行了。
    #[derive(Debug)]
    struct FooBar;
    
    #[derive(Debug)]
    struct BarFoo;
    
    //...
    impl PartialEq<FooBar> for FooBar{
        fn eq(&self, other: &FooBar) -> bool {
            true
        }
    }
    
    impl PartialEq<BarFoo> for BarFoo{
        fn eq(&self, other: &BarFoo) -> bool {
            true
        }
    }
    
    impl ops::Sub<Bar> for Foo {
        type Output = BarFoo;
    
        fn sub(self, rhs: Bar) -> Self::Output {
            BarFoo
        }
    }
    

    这代码的啰嗦程度堪比 Java ,但是说到底这种无意义的比较应该只存在练习题当中。

    5

    
    // 实现 `fn summary` 
    // 修复错误且不要移除任何代码行
    trait Summary {
        fn summarize(&self) -> String;
    }
    
    #[derive(Debug)]
    struct Post {
        title: String,
        author: String,
        content: String,
    }
    
    impl Summary for Post {
        fn summarize(&self) -> String {
            format!("The author of post {} is {}", self.title, self.author)
        }
    }
    
    #[derive(Debug)]
    struct Weibo {
        username: String,
        content: String,
    }
    
    impl Summary for Weibo {
        fn summarize(&self) -> String {
            format!("{} published a weibo {}", self.username, self.content)
        }
    }
    
    fn main() {
        let post = Post {
            title: "Popular Rust".to_string(),
            author: "Sunface".to_string(),
            content: "Rust is awesome!".to_string(),
        };
        let weibo = Weibo {
            username: "sunface".to_string(),
            content: "Weibo seems to be worse than Tweet".to_string(),
        };
    
        summary(post);
        summary(weibo);
    
        println!("{:?}", post);
        println!("{:?}", weibo);
    }
    
    // 在下面实现 `fn summary` 函数
    
    

    直接调用特征的方法即可:

    fn summary(porw: &impl Summary) -> String{
        porw.summarize()
    }
    

    哦对了,调用的地方需要改成借用,否则所有权转移后,后面打印不出来。

    6

    
    struct Sheep {}
    struct Cow {}
    
    trait Animal {
        fn noise(&self) -> String;
    }
    
    impl Animal for Sheep {
        fn noise(&self) -> String {
            "baaaaah!".to_string()
        }
    }
    
    impl Animal for Cow {
        fn noise(&self) -> String {
            "moooooo!".to_string()
        }
    }
    
    // 返回一个类型,该类型实现了 Animal 特征,但是我们并不能在编译期获知具体返回了哪个类型
    // 修复这里的错误,你可以使用虚假的随机,也可以使用特征对象
    fn random_animal(random_number: f64) -> impl Animal {
        if random_number < 0.5 {
            Sheep {}
        } else {
            Cow {}
        }
    }
    
    fn main() {
        let random_number = 0.234;
        let animal = random_animal(random_number);
        println!("You've randomly chosen an animal, and it says {}", animal.noise());
    }
    

    我还没学习特征对象,所以我决定直接改成假的随机

    fn random_animal(random_number: f64) -> impl Animal {
        if random_number < 0.5 {
            Sheep {}
        } else {
            Sheep {}
        }
    }
    

    7

    fn main() {
        assert_eq!(sum(1, 2), 3);
    }
    
    // 通过两种方法使用特征约束来实现 `fn sum`
    fn sum<T>(x: T, y: T) -> T {
        x + y
    }
    

    我就能想出一种方法:

    fn sum<T: Add<T, Output = T>>(x: T, y: T) -> T {
        x + y
    }
    

    8

    // 修复代码中的错误
    struct Pair<T> {
        x: T,
        y: T,
    }
    
    impl<T> Pair<T> {
        fn new(x: T, y: T) -> Self {
            Self {
                x,
                y,
            }
        }
    }
    
    impl<T: std::fmt::Debug + PartialOrd> Pair<T> {
        fn cmp_display(&self) {
            if self.x >= self.y {
                println!("The largest member is x = {:?}", self.x);
            } else {
                println!("The largest member is y = {:?}", self.y);
            }
        }
    }
    
    struct Unit(i32);
    
    fn main() {
        let pair = Pair{
            x: Unit(1),
            y: Unit(3)
        };
    
        pair.cmp_display();
    }
    

    本质上是Unit没有实现特征,加上就好了

    #[derive(Debug, PartialEq, PartialOrd)]
    struct Unit(i32);
    

    9

    
    // 填空
    fn example1() {
        // `T: Trait` 是最常使用的方式
        // `T: Fn(u32) -> u32` 说明 `T` 只能接收闭包类型的参数
        struct Cacher<T: Fn(u32) -> u32> {
            calculation: T,
            value: Option<u32>,
        }
    
        impl<T: Fn(u32) -> u32> Cacher<T> {
            fn new(calculation: T) -> Cacher<T> {
                Cacher {
                    calculation,
                    value: None,
                }
            }
    
            fn value(&mut self, arg: u32) -> u32 {
                match self.value {
                    Some(v) => v,
                    None => {
                        let v = (self.calculation)(arg);
                        self.value = Some(v);
                        v
                    },
                }
            }
        }
    
        let mut cacher = Cacher::new(|x| x+1);
        assert_eq!(cacher.value(10), __);
        assert_eq!(cacher.value(15), __);
    }
    
    
    fn example2() {
        // 还可以使用 `where` 来约束 T
        struct Cacher<T>
            where T: Fn(u32) -> u32,
        {
            calculation: T,
            value: Option<u32>,
        }
    
        impl<T> Cacher<T>
            where T: Fn(u32) -> u32,
        {
            fn new(calculation: T) -> Cacher<T> {
                Cacher {
                    calculation,
                    value: None,
                }
            }
    
            fn value(&mut self, arg: u32) -> u32 {
                match self.value {
                    Some(v) => v,
                    None => {
                        let v = (self.calculation)(arg);
                        self.value = Some(v);
                        v
                    },
                }
            }
        }
    
        let mut cacher = Cacher::new(|x| x+1);
        assert_eq!(cacher.value(20), __);
        assert_eq!(cacher.value(25), __);
    }
    
    
    
    fn main() {
        example1();
        example2();
    
        println!("Success!")
    }
    

    Cachervalue()实际上是,如果调用时Cacher没有value,就对value()的参数执行初始化时设定好的闭包;否则,就返回Cachervalue,无论参数的值是多少。

        assert_eq!(cacher.value(10), 11);
        assert_eq!(cacher.value(15), 11);
    
        assert_eq!(cacher.value(20), 21);
        assert_eq!(cacher.value(25), 21);
    
  • 相关阅读:
    如果我在初用tomcat时,是看到这篇tomcat架构解析,是不是就不会被说菜鸡了!
    Windows server 2012远程桌面会话主机和远程桌面授权
    5-4 jmu-报数游戏 (15分)
    题目0122-高效的任务规划
    python-flask笔记
    linux远程桌面管理工具xrdp
    Windows10打开应用总是会弹出提示窗口的解决方法
    java-net-php-python-IT在线教学平台录像计算机毕业设计程序
    小程序容器技术助力券商数字营销突围
    Python字典全解析:从基础到高级应用
  • 原文地址:https://blog.csdn.net/qq_37387199/article/details/143244702