Rust面向对象特性

news2024/11/14 13:27:28

文章目录

  • 封装
  • 基于特征对象vs基于泛型
    • 基于特征对象
    • 静态派遣和动态派遣
      • 静态派遣(Static Dispatch)
      • 动态派遣(Dynamic Dispatch)
    • 基于泛型
  • 状态设计模式
    • 面向对象的思想
    • rust思想:将状态和行为编码为类型(将状态编码成类型系统)
      • pub fn approve(self) 和 pub fn approve(&self)区别
      • pub fn approve(&self) 的含义
  • 参考

封装

pub struct AveragedCollection {
    // 结构体内部的成员缺省是私有的
    list: Vec<i32>,
    average: f64,
}

impl AveragedCollection {
    pub fn add(&mut self, value: i32) {
        self.list.push(value);
        self.update_average();
    }

    pub fn remove(&mut self) -> Option<i32> {
        let result = self.list.pop();
        match result {
            Some(value) => {
                self.update_average();
                Some(value)
            },
            None => None,
        }
    }

    pub fn average(&self) -> f64 {
        self.average
    }

    fn update_average(&mut self) {
        let total: i32 = self.list.iter().sum();
        self.average = total as f64 / self.list.len() as f64;
    }
}

基于特征对象vs基于泛型

pub trait Draw {
    fn draw(&self);
}

// 基于特征对象的实现,灵活的存储实现了某一个特征的类型的对象,有一些额外的开销
pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

// 基于泛型的实现,components存放的类型是同质的,没有额外的运行时的开销
pub struct Screen<T: Draw> {
    pub components: Vec<T>,
}

impl<T> Screen<T>
where
    T: Draw,
{
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

基于特征对象

lib.rs



pub trait Draw {
    fn draw(&self);
}

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}



pub struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

impl Draw for Button {
    fn draw(&self) {
        // 实际绘制按钮的代码
    }
}

main.rs

use gui::Draw;
use gui::{Button, Screen};
struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        // code to actually draw a select box
    }
}
fn main() {
    let screen = Screen {
        components: vec![
            Box::new(SelectBox {
                width: 75,
                height: 10,
                options: vec![
                    String::from("Yes"),
                    String::from("Maybe"),
                    String::from("No"),
                ],
            }),
            Box::new(Button {
                width: 50,
                height: 10,
                label: String::from("OK"),
            }),
        ],
    };

    screen.run();
}

静态派遣和动态派遣

在 Rust 中,静态派遣(Static Dispatch)和动态派遣(Dynamic Dispatch)是两种不同的调用方法,用于处理类型不确定的情况,尤其是在使用泛型和 trait 时。它们的主要区别在于如何确定函数调用时的具体类型,以及这一过程是在编译时还是运行时进行的。

静态派遣(Static Dispatch)

静态派遣是指在编译时就确定了函数调用的具体类型。Rust 会根据泛型的具体类型或 trait 对象的实现,生成直接调用相应方法的代码。所有这些决策都在编译时完成,因此效率较高。

如何实现:

  • 泛型(Generics):当你使用泛型时,Rust 会在编译时为每个使用的类型生成代码。
  • Trait Bound:通过在泛型中指定 trait 限定(比如 T: Trait),编译器可以为每种类型生成特定的代码,从而避免运行时的开销。
trait Speak {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Speak for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn greet<T: Speak>(animal: T) {
    animal.speak();
}

fn main() {
    let dog = Dog;
    let cat = Cat;
    
    greet(dog); // 静态派遣
    greet(cat); // 静态派遣
}

在这个例子中,greet 函数是泛型的,它接收任何实现了 Speak trait 的类型。Rust 会在编译时为 Dog 和 Cat 生成不同的代码,这就是静态派遣。

动态派遣(Dynamic Dispatch)

动态派遣是指函数调用的具体类型在运行时才决定。Rust 通过 trait 对象(即 dyn Trait)来实现动态派遣。当你使用 trait 对象时,Rust 会在运行时查找并调用合适的方法。

如何实现:

  • Trait 对象(Trait Object):使用 dyn Trait 来创建 trait 对象,这时会使用虚拟函数表(vtable)来实现方法的调用。这个 vtable 会在运行时生成,并根据实际类型查找合适的方法。
trait Speak {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Speak for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn greet(animal: &dyn Speak) {
    animal.speak();
}

fn main() {
    let dog = Dog;
    let cat = Cat;
    
    greet(&dog); // 动态派遣
    greet(&cat); // 动态派遣
}

在这个例子中,greet 函数接受一个 trait 对象 &dyn Speak。这意味着它在运行时会决定调用哪个类型的 speak 方法,而不是在编译时确定。

基于泛型

要基于泛型的方式改造代码,目标是避免使用 trait 对象(Box<dyn Draw>),而是直接使用泛型类型来提供类型安全和更高效的静态派遣。

通过这种方式,Screen 的 components 不再需要使用 Box<dyn Draw>,而是直接使用泛型参数 T,这样可以在编译时确定具体类型,避免了动态分发的开销。

lib.rs

// lib.rs

pub trait Draw {
    fn draw(&self);
}

pub struct Screen<T: Draw> {
    pub components: Vec<T>,
}

impl<T: Draw> Screen<T> {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

pub struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

impl Draw for Button {
    fn draw(&self) {
        // 实际绘制按钮的代码
        println!("Drawing Button: {}", self.label);
    }
}

main.rs

// main.rs
use gui::{Button, Draw, Screen};

struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        // 实际绘制选择框的代码
        println!("Drawing SelectBox with options: {:?}", self.options);
    }
}

fn main() {
    // 只能放一种类型
    let screen = Screen {
        components: vec![SelectBox {
            width: 75,
            height: 10,
            options: vec![
                String::from("Yes"),
                String::from("Maybe"),
                String::from("No"),
            ],
        }],
    };
    screen.run();
}

状态设计模式

状态模式(state pattern)是一个面向对象设计模式。该模式的关键在于一个值有某些内部状态,体现为一系列的 状态对象,同时值的行为随着其内部状态而改变。状态对象共享功能:当然,在 Rust 中使用结构体和 trait 而不是对象和继承。每一个状态对象负责其自身的行为,以及该状态何时应当转移至另一个状态。持有一个状态对象的值对于不同状态的行为以及何时状态转移毫不知情。

使用状态模式意味着当程序的业务需求改变时,无需修改保存状态值的代码或使用值的代码。

我们只需更新某个状态对象内部的代码,即可改变其规则,也可以增加更多的状态对象。

面向对象的思想

lib.rs

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        // as_ref()获取的是Option<&Box<dyn State>>
        // unwrap()返回的是&Box<dyn State>
        // 由于dref的自动转型,所以可以直接调用content()方法
        self.state.as_ref().unwrap().content(self)
    }
    pub fn request_review(&mut self) {
        // self.state.take取出状态(移走value)结构体中state还剩下None成员
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review()) //获取一个新的状态
        }
    }
    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    // 获取Self的所有权,返回State的特征对象
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
    fn content<'a>(&self, post: &'a Post) -> &'a str {
        ""
    }
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }
    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }
    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }

    // 入参有多个引用,输出参数也是引用,需要标注生命周期注解
    fn content<'a>(&self, post: &'a Post) -> &'a str {
        &post.content
    }
}

main.rs

use blog::Post;

fn main() {
    let mut post = Post::new();

    post.add_text("I ate a salad for lunch today");
    assert_eq!("", post.content());

    post.request_review();
    assert_eq!("", post.content());

    post.approve();
    assert_eq!("I ate a salad for lunch today", post.content());
}

编译

 cargo run
   Compiling blog v0.1.0 (/home/wangji/installer/rust/bobo/smartPtr)
warning: unused variable: `post`
  --> src/lib.rs:41:27
   |
41 |     fn content<'a>(&self, post: &'a Post) -> &'a str {
   |                           ^^^^ help: if this is intentional, prefix it with an underscore: `_post`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: `blog` (lib) generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 15.87s
     Running `target/debug/blog`

rust思想:将状态和行为编码为类型(将状态编码成类型系统)

对状态类型的编译直接移动到了结构体中了

lib.rs

pub struct Post {
    content: String,
}

pub struct DraftPost {
    content: String,
}

impl Post {
    pub fn new() -> DraftPost {
        DraftPost {
            content: String::new(),
        }
    }

    pub fn content(&self) -> &str {
        &self.content
    }
}

impl DraftPost {
    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn request_review(self) -> PendingReviewPost {
        PendingReviewPost {
            content: self.content,
        }
    }
}

pub struct PendingReviewPost {
    content: String,
}

impl PendingReviewPost {
    pub fn approve(self) -> Post {
        Post {
            content: self.content,
        }
    }
}

main.rs

use blog::Post;

fn main() {
    let mut post = Post::new();

    post.add_text("I ate a salad for lunch today");

    let post = post.request_review();

    let post = post.approve();

    assert_eq!("I ate a salad for lunch today", post.content());
}

pub fn approve(self) 和 pub fn approve(&self)区别

pub fn approve(self) 的含义

  • 所有权转移:当函数签名中使用 self 时,意味着该方法获得了 self 的所有权。这意味着 self 的所有权被移动到函数内部,并且调用该方法后,原来的对象将无法再被使用。

  • 适用场景:当你希望在方法中消费 self,即你不再需要继续使用调用者实例时,通常会使用 self。例如,你可能会对对象进行销毁、修改或替换,或者将其传递给其他地方。

struct User {
    name: String,
}

impl User {
    pub fn approve(self) {
        println!("Approving user: {}", self.name);
        // 此时 `self` 的所有权已被转移,外部无法再使用 `User` 实例
    }
}

fn main() {
    let user = User { name: String::from("Alice") };
    user.approve(); // 这里 `user` 的所有权被转移给了 `approve` 方法
    // println!("{}", user.name); // 这将无法编译,因为 `user` 的所有权已被转移
}

pub fn approve(&self) 的含义

借用:当函数签名中使用 &self 时,表示方法是对 self 的 借用。这意味着方法不会获取对象的所有权,它只会借用 self,允许你在方法内部使用对象,但调用方法后,self 仍然可用。

适用场景:当你只需要读取 self 中的数据,或者只是对对象进行某些操作而不修改或销毁它时,使用 &self。借用可以保证你不会错误地修改原对象或消耗掉它。

struct User {
    name: String,
}

impl User {
    pub fn approve(&self) {
        println!("Approving user: {}", self.name);
        // 这里仅借用了 `self`,所以 `self` 的所有权没有被转移
    }
}

fn main() {
    let user = User { name: String::from("Alice") };
    user.approve(); // 借用 `user` 并调用方法
    println!("{}", user.name); // 仍然可以继续使用 `user`
}

参考

  • 第17章~Rust和面向对象特性

  • Rust 的面向对象特性

  • 面向对象设计模式的实现

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

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

相关文章

Qt滑动条美化自定义

效果展示 主要代码 头文件 下面是hi控件的头文件&#xff0c;我们继承一个Qt原生的滑动条类QSlider&#xff0c;然后在基类的基础上进行自定义&#xff0c;我会对重要的变量进行解析&#xff1a; class XSlider : public QSlider {Q_OBJECT public:explicit XSlider(QWidget…

wordpress实用功能A5资源网同款 隐藏下载框 支付框 需要登录才能查看隐藏的内容

实用功能 隐藏下载框 支付框 需要登录才能查看隐藏的内容, 个人网站防天朝申查实测有效 。 登录前&#xff0c;未登录&#xff1a; 登录后&#xff0c;已登录&#xff1a; 功能说明 该代码段的主要功能是隐藏支付框并为未 登录用户显示一条提示信息&#xff0c;告知他们需要…

Vue 简单入手

前端工程化&#xff08;Front-end Engineering&#xff09;指的是在前端开发中&#xff0c;通过一系列工具、流程和规范的整合&#xff0c;以提高开发效率、代码质量和可维护性的一种技术和实践方法。其核心目的是使得前端开发变得更高效、可扩展和可维护。 文章目录 一、Vue 项…

Spingboot 定时任务与拦截器(详细解释)

在 boot 环境中&#xff0c;一般来说&#xff0c;要实现定时任务&#xff0c;我们有两中方案&#xff0c;一种是使用 Spring 自带的定时 任务处理器 Scheduled 注解&#xff0c;另一种就是使用第三方框架 Quartz &#xff0c; Spring Boot 源自 SpringSpringMVC &#…

无人机+无人车+无人狗+无人船:互通互联技术探索详解

关于“无人机无人车机器狗&#xff08;注&#xff1a;原文中的“无人狗”可能是一个笔误&#xff0c;因为在实际技术领域中&#xff0c;常用的是“机器狗”这一术语&#xff09;无人船”的互通互联技术&#xff0c;以下是对其的详细探索与解析&#xff1a; 一、系统架构与关键…

ima.copilot-腾讯智能工作台

一、产品描述 ima.copilot是腾讯推出的基于腾讯混元大模型技术的智能工作台&#xff0c;通过先进的人工智能技术&#xff0c;为用户提供了一个全新的搜读写体验&#xff0c;让知识管理变得更加智能和高效。它不仅是一个工具&#xff0c;更是一个智能的伙伴&#xff0c;能够帮助…

集合卡尔曼滤波(EnsembleKalmanFilter)的MATLAB例程(三维、二维)

本 M A T L A B MATLAB MATLAB代码实现了一个三维动态系统的集合卡尔曼滤波&#xff08;Ensemble Kalman Filter, EnKF&#xff09;示例。代码的主要目的是通过模拟真实状态和测量值&#xff0c;使用 EnKF 方法对动态系统状态进行估计。 文章目录 参数设置初始化真实状态定义状…

【动手学电机驱动】STM32-FOC(5)基于 IHM03 的无感 FOC 控制

STM32-FOC&#xff08;1&#xff09;STM32 电机控制的软件开发环境 STM32-FOC&#xff08;2&#xff09;STM32 导入和创建项目 STM32-FOC&#xff08;3&#xff09;STM32 三路互补 PWM 输出 STM32-FOC&#xff08;4&#xff09;IHM03 电机控制套件介绍 STM32-FOC&#xff08;5&…

光老化测试的三种试验:紫外老化、氙灯老化、碳弧灯老化

光老化是指材料在阳光照射下&#xff0c;由于紫外线、热和氧气的共同作用而发生的物理和化学变化。这种现象对纺织材料、塑料材料、涂料材料和橡胶材料的应用有显著影响。这些材料户外家具、汽车内饰和户外供水排水管道、建筑外墙涂料、汽车漆面、船舶涂料、汽车轮胎、密封件、…

VMWare安装包及安装过程

虚拟机基本使用 检查自己是否开启虚拟化 如果虚拟化没有开启&#xff0c;需要自行开启&#xff1a;百度加上自己电脑的品牌型号&#xff0c;进入BIOS界面开启 什么是虚拟机 所谓的虚拟机&#xff0c;就是在当前计算机系统中&#xff0c;又开启了一个虚拟系统 这个虚拟系统&…

消费的外部性

大学宿舍&#xff0c;遇到在你睡觉的时候开外放不戴耳机的室友&#xff0c;但中午12点&#xff0c;室友却在那拉上窗帘睡觉。能带饭吗&#xff1f;能代签到吗&#xff1f;能倒个垃圾吗&#xff1f;能带个外卖吗&#xff1f;自己永远麻烦别人&#xff0c;你要让他帮个忙又这推那…

易趋亮相2024 PMI项目管理大会

11月9日-10日&#xff0c;2024 PMI项目管理大会在广州圆满举办&#xff0c;项目管理行业优秀代表企业——易趋&#xff08;隶属深圳市蓝云软件有限公司&#xff09;&#xff0c;携最新产品和解决方案亮相本次展会。 (主论坛现场&#xff09; 本届大会以“‘项’有所成 行以致远…

边缘计算与推理算力:智能时代的加速引擎

在数据量爆炸性增长的今天&#xff0c;边缘计算与推理算力正成为推动智能应用的关键力量。智能家居、自动驾驶、工业4.0等领域正在逐步从传统的云端计算转向边缘计算&#xff0c;而推理算力的加入&#xff0c;为边缘计算提供了更强的数据处理能力和实时决策能力。本文将探讨边缘…

基于matlab的CNN食物识别分类系统,matlab深度学习分类,训练+数据集+界面

文章目录 前言&#x1f393;一、数据集准备&#x1f393;二、模型训练&#x1f340;&#x1f340;1.初始化&#x1f340;&#x1f340;2.加载数据集&#x1f340;&#x1f340;3.划分数据集&#xff0c;并保存到新的文件夹&#x1f340;&#x1f340;4.可视化数据集&#x1f34…

马斯克万卡集群AI数据中心引发的科技涟漪:智算数据中心挑战与机遇的全景洞察

一、AI 爆发重塑数据中心格局 随着AI 技术的迅猛发展&#xff0c;尤其是大模型的崛起&#xff0c;其对数据中心产生了极为深远的影响。大模型以其数以亿计甚至更多的参数和对海量数据的处理需求&#xff0c;成为了 AI 发展的核心驱动力之一&#xff0c;同时也为数据中心带来了…

移远通信亮相骁龙AI PC生态科技日,以领先的5G及Wi-Fi产品革新PC用户体验

PC作为人们学习、办公、娱乐的重要工具&#xff0c;已经深度融入我们的工作和生活。随着物联网技术的快速发展&#xff0c;以及人们对PC性能要求的逐步提高&#xff0c;AI PC成为了行业发展的重要趋势。 11月7-8日&#xff0c;骁龙AI PC生态科技日在深圳举办。作为高通骁龙的重…

Unity资源打包Addressable资源保存在项目中

怎么打包先看“Unity资源打包Addressable AA包” 其中遗留一个问题&#xff0c;下载下来的资源被保存在C盘中了&#xff0c;可不可以保存在项目中呢&#xff1f;可以。 新建了一个项目&#xff0c;路径与“Unity资源打包Addressable AA包”都不相同了 1.创建资源缓存路径 在…

postman变量和脚本功能介绍

1、基本概念——global、collection、environment 在postman中&#xff0c;为了更好的管理各类变量、测试环境以及脚本等&#xff0c;创建了一些概念&#xff0c;包括&#xff1a;globals、collection、environment。其实在postman中&#xff0c;最上层还有一个Workspaces的概…

为什么汽车电源正在用 48V 取代 12V

欧姆定律也有利于 48 伏电源 假设您需要为汽车的起动电机供电。可能存在以下静态和动态特征&#xff1a; 电源电压&#xff1a;12V 额定电流&#xff1a;40A 额定功率&#xff1a;480W 标称平均阻抗&#xff1a;0.3Ω 浪涌电流&#xff1a;150A 浪涌功率&#xff1a;1,8…

【webrtc】 RTP 中的 MID(Media Stream Identifier)

RTP 中的 MID(Media Stream Identifier) RID及其与MID的区别 cname与mid的对比【webrtc】CNAME 是rtprtcp中的Canonical Name(规范化名称) 同样都是RTP头部扩展: 基于mediasoup的最新的代码,学习,发现mid在创建RtpSendStream时是必须传递的参数: 例如 D:\XTRANS\soup\…