【Rust练习】18.特征 Trait

news2024/10/27 19:23:19

练习题来自: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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2224873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

241026-RHEL如何以root身份卸载Docker

在 RHEL 8.8 中&#xff0c;以 root 身份卸载 Docker 可以通过以下步骤完成&#xff1a; 停止 Docker 服务&#xff08;如果已启动&#xff09;&#xff1a; sudo systemctl stop docker删除 Docker 包&#xff1a; 运行以下命令卸载 Docker 引擎及其依赖包&#xff08;docker-…

LeetCode 107.二叉树的层次遍历 II

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[1…

HBuilderX离线打包Android

HBuilderX离线打包Android 前言1.Android 离线SDK2.UniApp程序3.DCloud后台设置4.AndroidStudio打包&#xff08;1&#xff09;Import Project&#xff08;2&#xff09;AndroidManifest.xml&#xff08;3&#xff09;dcloud_control.xml&#xff08;4&#xff09;签名&#xf…

海外云手机实现高效的海外社交媒体营销

随着全球化的深入发展&#xff0c;越来越多的中国企业走向国际市场&#xff0c;尤其是B2B外贸企业&#xff0c;海外社交媒体营销已成为其扩大市场的重要手段。在复杂多变的海外市场环境中&#xff0c;如何有效提高营销效率并降低运营风险&#xff0c;成为了众多企业的首要任务。…

三周精通FastAPI:14 表单数据和表单模型Form Models

官网文档&#xff1a;表单数据 - FastAPI 表单数据 接收的不是 JSON&#xff0c;而是表单字段时&#xff0c;要使用 Form表单。 from fastapi import FastAPI, Formapp FastAPI()app.post("/login/") async def login(username: str Form(), password: str Form…

【Nginx系列】如何使用 proxy_ignore_client_abort 提升性能

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

多系统萎缩的探索与实践?

‌在神经系统疾病的广阔领域中&#xff0c;多系统萎缩以其复杂的病因和难治的特点&#xff0c;一直是医学界关注的焦点。刘家峰大夫&#xff0c;出生中医世家&#xff0c;对多系统萎缩的治疗有着独到的见解和丰富的实践经验。 刘家峰大夫认为&#xff0c;多系统萎缩虽表现为多…

OpenAI低调发布多智能体工具Swarm:让多个智能体协同工作!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

爬虫中代理ip 的选择和使用实战

一、爬虫中的反爬问题 爬虫技术不仅是一种工具&#xff0c;更像是一门捕捉信息的艺术。通过它&#xff0c;我们能够从浩瀚的互联网中&#xff0c;精确获取到所需的有价值数据。对于那些需要进行数据分析或模型训练的人来说&#xff0c;爬虫技术几乎是必备的技能。虽然网上公开…

springboot083基于springboot的个人理财系统--论文pf(论文+源码)_kaic

基于springboot的个人理财系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了个人理财系统的开发全过程。通过分析个人理财系统管理的不足&#xff0c;创建了一个计算机管理个人理财系统的方案。文章介绍了个…

RestHighLevelClient操作es查询文档

目录 利用RestHighLevelClient客户端操作es查询文档 查询match_all dsl语句&#xff1a; ​编辑 java代码 小结 match字段全文检索查询 dsl语句 java代码 multi_match多字段全文检索查询 dsl语句 java代码 term精确查询 dsl语句 java代码 range范围查询 dsl语句 j…

信息学奥赛与其他四大奥赛的区别:深入分析与解读

五大学科奥赛&#xff0c;涵盖了信息学、数学、物理、化学和生物&#xff0c;每一学科竞赛的重点和考查方式都不同。信息学奥赛&#xff08;NOI&#xff09;与其他四科相比&#xff0c;独具编程和算法特性。本文将深入分析信息学奥赛与其他四科竞赛在考查内容、备赛方式、实践要…

《BLEU: a Method for Automatic Evaluation of Machine Translation》翻译

文章目录 0. 摘要1. 引言1.1 理由1.2 观点 2. 基准 BLEU 指标2.1 修正的 n-gram 精度2.1.1 对文本块的修正 n-gram 精度2.1.2 仅使用修正 n-gram 精度对系统进行排序2.1.3 结合修正的 n-gram 精度 2.2 句子长度2.2.1 召回率的问题2.2.2 句子简短惩罚 2.3 BLEU 细节 3. BLEU 评估…

芒果数据集(猫脸码客第230期)

芒果深度学习检测&#xff1a;开启农业新视界 一、引言 芒果作为一种重要的热带水果&#xff0c;在全球范围内具有广泛的市场需求和经济价值。随着人们生活水平的提高&#xff0c;对芒果的品质要求也越来越高。芒果产业的规模不断扩大&#xff0c;产量逐年增加&#xff0c;如…

中间件安全(三)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 前言: 本文主要讲解apache命令执行漏洞&#xff08;cve_2021_41773&#xff09;。 靶场链接&#xff1a;Vulfocus 漏洞威胁分析平台 一&#xff0c;漏洞简介。 cve_2021_41773漏洞…

【Linux探索学习】第一弹——Linux的常用指令

目录 ​编辑 Linux常用命令 1 Linux命令初体验 1.1 常用命令演示 1). ls 2). pwd 3). cd 4). touch 5). mkdir 6). rm 1.2 Linux命令使用技巧 1.3 Linux命令格式 2 文件目录操作命令 2.1 ls 2.2 cd 2.3 cat 2.4 more 2.5 tail 2.6 mkdir 2.7 rmdir 2.8 rm…

《Windows PE》7.4 资源表应用

本节我们将通过两个示例程序&#xff0c;演示对PE文件内图标资源的置换与提取。 本节必须掌握的知识点&#xff1a; 更改图标 提取图标资源 7.4.1 更改图标 让我们来做一个实验&#xff0c;替换PE文件中现有的图标。如果手工替换&#xff0c;一定是先找到资源表&#xff0c;…

Telephony中ITelephony的AIDL调用关系

以Android14.0源码讲解 ITelephony来自framework下的com.android.internal.telephony包下 frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl这个接口用于与Phone交互的界面&#xff0c;主要由TelephonyManager类使用&#xff0c;一些地方仍在…

开拓鸿蒙测试新境界,龙测科技引领自动化测试未来

在当今科技舞台上&#xff0c;鸿蒙 OS 以非凡先进性强势登场&#xff0c;打破传统操作系统格局&#xff0c;为软件测试领域带来全新机遇与艰巨挑战。 一、鸿蒙 OS 的辉煌崛起 &#xff08;一&#xff09;壮丽发展历程与卓越市场地位 鸿蒙 OS 的发展如波澜壮阔的史诗。2023 年…

音视频开发之旅(98) -潜扩散模型(Latent Diffusion Model)原理及源码解析

目录 1.背景 2. 潜扩散模型&#xff08;Latent Diffusion Model&#xff09;原理 3. 应用场景 4. 推理源码解析 5. 资料 一、背景 前面我们分析扩散模型&#xff08;Diffusion Model&#xff09;了解到&#xff0c;它通过向数据中添加噪声&#xff0c;然后训练一个去噪模…