【Rust练习】20.进一步深入特征

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

1

rust 复制代码
struct Container(i32, i32);

// 使用关联类型实现重新实现以下特征
// trait Contains {
//    type A;
//    type B;

trait Contains<A, B> {
    fn contains(&self, _: &A, _: &B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

impl Contains<i32, i32> for Container {
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    // Grab the first number.
    fn first(&self) -> i32 { self.0 }

    // Grab the last number.
    fn last(&self) -> i32 { self.1 }
}

fn difference<A, B, C: Contains<A, B>>(container: &C) -> i32 {
    container.last() - container.first()
}

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

    let container = Container(number_1, number_2);

    println!("Does container contain {} and {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("First number: {}", container.first());
    println!("Last number: {}", container.last());
    
    println!("The difference is: {}", difference(&container));
}

所谓关联类型,个人觉得更像是一种语法糖。它将本来需要用泛型表示的类型写在特征里面。实现特征,不仅需要实现特征中的方法,还要实现特征中的类型。

将A和B作为关联类型写入特征:

rust 复制代码
trait Contains{
    type A;
    type B;
    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

实现特征时,显式指定A和B的类型:

rust 复制代码
impl Contains for Container {
    type A = i32;
    type B = i32;
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    // Grab the first number.
    fn first(&self) -> i32 { self.0 }

    // Grab the last number.
    fn last(&self) -> i32 { self.1 }
}

无需在上层指定类型,使用者暗含了类型:

rust 复制代码
fn difference<C: Contains>(container: &C) -> i32 {
    container.last() - container.first()
}

2

rust 复制代码
use std::ops::Sub;

#[derive(Debug, PartialEq)]
struct Point<T> {
    x: T,
    y: T,
}

// 用三种方法填空: 其中两种使用默认的泛型参数,另外一种不使用
impl __ {
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

fn main() {
    assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
        Point { x: 1, y: 3 });

    println!("Success!")
}

这题目是第一个我没做出来,然后不得不求助答案的题目... ...这三种语法是逐渐变简单的,第一种是最复杂的,核心是impl T,但是对T进行Sub的特征限定,而且输出必须也是T(否则后面xy无法相减);其次Sub的类型需要是Point,最后Point也需要泛型参数。

rust 复制代码
impl<T: Sub<Output = T>> Sub<Point<T>> for Point<T> {
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

由于Point就是Self,所以这就是第二种语法:

rust 复制代码
impl<T: Sub<Output = T>> Sub<Self> for Point<T> {
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

Sub本身的泛型参数是存在默认值的,就是Self,因此这里可以继续省略:

rust 复制代码
impl<T: Sub<Output = T>> Sub for Point<T> {
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Point {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

3

rust 复制代码
trait Pilot {
    fn fly(&self) -> String;
}

trait Wizard {
    fn fly(&self) -> String;
}

struct Human;

impl Pilot for Human {
    fn fly(&self) -> String {
        String::from("This is your captain speaking.")
    }
}

impl Wizard for Human {
    fn fly(&self) -> String {
        String::from("Up!")
    }
}

impl Human {
    fn fly(&self) -> String {
        String::from("*waving arms furiously*")
    }
}

fn main() {
    let person = Human;

    assert_eq!(__, "This is your captain speaking.");
    assert_eq!(__, "Up!");

    assert_eq!(__, "*waving arms furiously*");

    println!("Success!")
}

这里涉及到一个多继承的问题,即一个类同时实现了两个特征,其中各有一个签名相同的方法,类本身也有一个签名相同的方法,需要加限定符号区分。

rust 复制代码
fn main() {
    let person = Human;

    assert_eq!(Pilot::fly(&person), "This is your captain speaking.");
    assert_eq!(Wizard::fly(&person), "Up!");

    assert_eq!(Human::fly(&person), "*waving arms furiously*");

    println!("Success!")
}

当然,自己调用自己的方法还有更简单,也是更常见的写法:

rust 复制代码
assert_eq!(person.fly(), "*waving arms furiously*");

C++里也存在多继承的问题,虽然就我的工作经验来看很少出现,比如下面的代码:

cpp 复制代码
struct P1
{
    virtual void get()
    {
        cout << "P1 get" << endl;
    }
};

struct P2
{
    virtual void get()
    {
        cout << "P2 get" << endl;
    }
};

struct Child: P1, P2
{
    // virtual void get()
    // {
    //     cout << "Child get" << endl;
    // }
};

int main()
{
    Child child;
    child.get();
}

这里的get就会出现歧义的问题。解决办法也是加上限定符:

cpp 复制代码
int main()
{
    Child child;
    child.P1::get();
    child.P2::get();
}

直接像Rust那么写肯定是不行的,这种直接用双冒号开头的写法,在C++中是静态方法(static)的特权。

4

rust 复制代码
trait Person {
    fn name(&self) -> String;
}

// Person 是 Student 的 supertrait .
// 实现 Student 需要同时实现 Person.
trait Student: Person {
    fn university(&self) -> String;
}

trait Programmer {
    fn fav_language(&self) -> String;
}

// CompSciStudent (computer science student) 是 Programmer 
// 和 Student 的 subtrait. 实现 CompSciStudent 需要先实现这两个 supertraits.
trait CompSciStudent: Programmer + Student {
    fn git_username(&self) -> String;
}

fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
    format!(
        "My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
        student.name(),
        student.university(),
        student.fav_language(),
        student.git_username()
    )
}

struct CSStudent {
    name: String,
    university: String,
    fav_language: String,
    git_username: String
}

// 为 CSStudent 实现所需的特征
impl ...

fn main() {
    let student = CSStudent {
        name: "Sunfei".to_string(),
        university: "XXX".to_string(),
        fav_language: "Rust".to_string(),
        git_username: "sunface".to_string()
    };

    // 填空
    println!("{}", comp_sci_student_greeting(__));
}

逐个实现特征即可,不过Rust居然不能一次实现所有的特征(感觉这些特征方法也可以写在同一个里)

rust 复制代码
impl Person for CSStudent {
    fn name(&self) -> String {
        self.name.clone()
    }
}

impl Student for CSStudent{
    fn university(&self) -> String{
        self.university.clone()
    }
}

impl Programmer for CSStudent {
    fn fav_language(&self) -> String {
        self.fav_language.clone()
    }
}

impl CompSciStudent for CSStudent{
    fn git_username(&self) -> String{
        self.git_username.clone()
    }
}

5

rust 复制代码
use std::fmt;

// 定义一个 newtype `Pretty`


impl fmt::Display for Pretty {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\"{}\"", self.0.clone() + ", world")
    }
}

fn main() {
    let w = Pretty("hello".to_string());
    println!("w = {}", w);
}

这玩意让我觉得孤儿规则没什么意义。。。

rust 复制代码
use std::fmt;

// 定义一个 newtype `Pretty`
struct Pretty(String);

impl fmt::Display for Pretty {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\"{}\"", self.0.clone() + ", world")
    }
}

fn main() {
    let w = Pretty("hello".to_string());
    println!("w = {}", w);
}
相关推荐
_OLi_4 分钟前
力扣 LeetCode 19. 删除链表的倒数第N个结点(Day2:链表)
算法·leetcode·链表
豆沙沙包?5 分钟前
LC68----222. 完全二叉树的节点个数(java版)---树
java·开发语言
几何DDDMm14 分钟前
Python自动化测试---Appium
开发语言·python·appium
MATLAB滤波15 分钟前
鲁棒自适应滤波,MATLAB
开发语言·算法·matlab
闲人编程23 分钟前
爬虫反爬机制和解决方案
开发语言·c++·爬虫·python·验证码
985小水博一枚呀24 分钟前
【深度学习目标检测|YOLO算法4-4】YOLO家族进化史:从YOLOv1到YOLOv11的架构创新、性能优化与行业应用全解析——工业领域
网络·人工智能·深度学习·算法·yolo·目标检测·架构
xianwu54331 分钟前
【设计模式】工厂模式
开发语言·c++·设计模式·简单工厂模式·抽象工厂模式
。小二33 分钟前
前端js用canvas合成图片并转file对象
开发语言·前端·javascript·canva可画
摆烂仙君33 分钟前
matlab建模入门指导
开发语言·matlab
ZhangTao_zata42 分钟前
从python源码到可自动更新软件
linux·开发语言·python