欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 【Rust练习】18.特征 Trait

【Rust练习】18.特征 Trait

2024/12/28 10:37:34 来源:https://blog.csdn.net/qq_37387199/article/details/143244702  浏览:    关键词:【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 multiplyfn 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` 来约束 Tstruct 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);

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com