当前位置: 首页 > news >正文

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

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

1


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作为关联类型写入特征:

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

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

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 }
}

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

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

2


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也需要泛型参数。

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,所以这就是第二种语法:

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,因此这里可以继续省略:

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

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!")
}

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

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!")
}

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

assert_eq!(person.fly(), "*waving arms furiously*");

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

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就会出现歧义的问题。解决办法也是加上限定符:

int main()
{Child child;child.P1::get();child.P2::get();
}

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

4


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居然不能一次实现所有的特征(感觉这些特征方法也可以写在同一个里)

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

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);
}

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

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);
}

http://www.mrgr.cn/news/69788.html

相关文章:

  • ⚡️如何在 React 和 Next.js 项目里优雅的使用 Zustand
  • 微服务学习重点:底层的实现逻辑
  • Spring boot + Vue2小项目基本模板
  • 使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径
  • DAY110代码审计-PHP框架开发篇ThinkPHP版本缺陷不安全写法路由访问利用链
  • Linux开发讲课49--- Linux 启动过程分析
  • Debezium系列之:Incremental snapshotting设计原理
  • 临床预测模型-静态诺模/列线图(Nomogram)+校准曲线(Calibration)分析学习
  • 动态规划-两个数组的dp问题——718.最长重复子数组
  • 【leetcode练习·二叉树】用「分解问题」思维解题 I
  • 《PyTorch深度学习快速入门教程》学习笔记(第20周)
  • 计算机网络基本概念总结
  • cherno引擎课 -
  • 计算机网络-1.2分层结构
  • PostgreSQL 开启密码验证插件
  • 医学图像算法之基于Unet的视网膜血管分割
  • 【Lucene】从文本到索引:Lucene如何构建索引
  • 伊洛瓦底江
  • 存贷款调整 20241110
  • Linux进程信号
  • “穿梭于容器之间:C++ STL迭代器的艺术之旅”
  • 【CLIP系列】开篇
  • GIN:逼近WL-test的GNN架构
  • 信息泄露漏洞一文速通
  • 【Hadoop实训】Hive 数据操作①
  • 全面解析 Python typing模块与静态类型注解:从基础到高级