Rust编程语言入门之Rust的面向对象编程特性

这篇具有很好参考价值的文章主要介绍了Rust编程语言入门之Rust的面向对象编程特性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Rust 的面向对象编程特性

一、面向对象语言的特性

Rust是面向对象编程语言吗?

  • Rust 受到多种编程范式的影响,包括面向对象
  • 面向对象通常包含以下特性:命名对象、封装、继承

对象包含数据和行为

  • “设计模式四人帮”在《设计模型》中给面向对象的定义:
    • 面向对象的程序由对象组成
    • 对象包装了数据和操作这些数据的过程,这些过程通常被称作方法或操作
  • 基于此定义:Rust是面向对象的
    • struct、enum 包含数据
    • impl 块为之提供了方法
    • 但带有方法的 struct、enum 并没有被称为对象

封装

  • 封装:调用对象外部的代码无法直接访问对象内部的实现细节,唯一可以与对象进行交互的方法就是通过它公开的 API
  • Rust:pub 关键字
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;
  }
}

继承

  • 继承:使对象可以沿用另外一个对象的数据和行为,且无需重复定义相关代码
  • Rust:没有继承
  • 使用继承的原因:
    • 代码复用
      • Rust:默认 trait 方法来进行代码共享
    • 多态
      • Rust:泛型和 trait 约束(限定参数化多态 bounded parametric)
  • 很多新语言都不使用继承作为内置的程序设计方案了。

二、使用 trait 对象来存储不同类型的值

有这样一个需求

  • 创建一个 GUI 工具:
    • 它会遍历某个元素的列表,依次调用元素的 draw 方法进行绘制
    • 例如:Button、TextField 等元素
  • 在面向对象语言里:
    • 定义一个 Component 父类,里面定义了 draw 方法
    • 定义 Button、TextField 等类,继承与 Component 类

为共有行为定义一个 trait

  • Rust 避免将 struct 或 enum 称为对象,因为他们与 impl 块是分开的
  • trait 对象有些类似于其它语言中的对象:
    • 它们某种程度上组合了数据与行为
  • trait 对象与传统对象不同的地方:
    • 无法为 trait 对象添加数据
  • trait 对象被专门用于抽象某些共有行为,它没其它语言中的对象那么通用

Trait 动态 lib.rs 文件

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

pub struct Screen {
  pub components: Vec<Boc<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) {
    // 绘制一个按钮
  }
}

泛型的实现 一次只能实现一个类型

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

main.rs 文件

use oo::Draw;
use oo::{Button, Screen};

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

impl Draw for SelectBox {
  fn draw(&self) {
    // 绘制一个选择框
  }
}

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

Trait 对象执行的是动态派发

  • 将 trait 约束作用于泛型时,Rust编译器会执行单态化:
    • 编译器会为我们用来替换泛型参数的每一个具体类型生成对应函数和方法的非泛型实现。
  • 通过单态化生成的代码会执行静态派发(static dispatch),在编译过程中确定调用的具体方法
  • 动态派发(dynamic dispatch):
    • 无法在编译过程中确定你调用的究竟是哪一种方法
    • 编译器会产生额外的代码以便在运行时找出希望调用的方法
  • 使用 trait 对象,会执行动态派发:
    • 产生运行时开销
    • 阻止编译器内联方法代码,使得部分优化操作无法进行

Trait 对象必须保证对象安全

  • 只能把满足对象安全(object-safe)的 trait 转化为 trait 对象
  • Rust采用一系列规则来判定某个对象是否安全,只需记住两条:
    • 方法的返回类型不是 Self
    • 方法中不包含任何泛型类型参数

lib.rs 文件

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

pub trait Clone {
  fn clone(&self) -> Self;
}

pub struct Screen {
  pub components: Vec<Box<dyn Clone>>, // 报错
}

三、实现面向对象的设计模式

状态模式

  • 状态模式(state pattern)是一种面向对象设计模式:
    • 一个值拥有的内部状态由数个状态对象(state object)表达而成,而值的行为则随着内部状态的改变而改变
  • 使用状态模式意味着:
    • 业务需求变化时,不需要修改持有状态的值的代码,或者使用这个值的代码
    • 只需要更新状态对象内部的代码,以便改变其规则,或者增加一些新的状态对象

例子:发布博客的工作流程 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());
}

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 {
    ""
  }
  
  pub fn request_review(&mut self) {
    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 {
  fn request_review(self: Box<Self>) -> Box<dyn State>;
  fn approve(self: Box<Self>) -> Box<dyn State>;
}

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

修改之后:

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 {
    self.state.as_ref().unwrap().content(&self)
  }
  
  pub fn request_review(&mut self) {
    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 {
  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 PendingRevew {
  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
  }
}

状态模式的取舍权衡

  • 缺点:
    • 某些状态之间是相互耦合的
    • 需要重复实现一些逻辑代码

将状态和行为编码为类型

  • 将状态编码为不同的类型:
    • 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 代码:文章来源地址https://www.toymoban.com/news/detail-419898.html

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

总结

  • Rust 不仅能够实现面向对象的设计模式,还可以支持更多的模式
  • 例如:将状态和行为编码为类型
  • 面向对象的经典模式并不总是 Rust 编程实践中的最佳选择,因为 Rust具有所有权等其它面向对象语言没有的特性!

到了这里,关于Rust编程语言入门之Rust的面向对象编程特性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go语言面向对象编程

    注:安装教程 上一篇 1.1、简单实例 1.2、指针 1、Go语言中的面向对象最为直观,也无需支付额外的成本。如果要求对象必须以指针传递,这有时会是个额外成本,因为对象有时很小(比如4字节),用指针传递并不划算。 只有在你需要修改对象的时候,才必须用指针。它不是

    2024年02月06日
    浏览(56)
  • 【转载】R语言 面向对象编程

    转载自:R语言教程 面向对象的编程侧重于数据和对象,而不是程序。面向对象的模型有助于我们对现实生活中的对象进行建模。为了在数据科学领域出类拔萃,掌握面向对象的编程概念很重要。每个程序都有特殊类型的类。在本教程中,将重点讨论R语言中的S3和S4类、泛型函

    2024年01月20日
    浏览(117)
  • 【JAVA】面向对象的编程语言(继承篇)

    个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 在之前的文章中,我们介绍过面向对象的编程语言,今天我们就来就进入到JAVA面对对象的编程世界,今天我们主要来介绍面向对象的编程范式中一个重要的概念——继承。 继承是java面向对象编程技术的一块基石,因为

    2024年02月09日
    浏览(45)
  • 【跟小嘉学 Rust 编程】十三、函数式语言特性:迭代器和闭包

    【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举

    2024年02月11日
    浏览(59)
  • 【Go 基础篇】走进Go语言的面向对象编程世界

    欢迎各位编程爱好者们!今天我们将进入Go语言的面向对象编程(OOP)世界,一窥这门语言如何运用OOP思想来组织和构建程序。无论你是初学者还是有一些经验的开发者,本文都将为你揭示Go语言中的OOP特性、方法和最佳实践。 面向对象编程是一种程序设计范式,它以对象为

    2024年02月10日
    浏览(54)
  • Rust编程语言入门之模式匹配

    模式是Rust中的一种特殊语法,用于匹配复杂和简单类型的结构 将模式与匹配表达式和其他构造结合使用,可以更好地控制程序的控制流 模式由以下元素(的一些组合)组成: 字面值 解构的数组、enum、struct 和 tuple 变量 通配符 占位符 想要使用模式,需要将其与某个值进行

    2023年04月22日
    浏览(92)
  • Rust编程语言入门之智能指针

    指针:一个变量在内存中包含的是一个地址(指向其它数据) Rust 中最常见的指针就是”引用“ 引用: 使用 借用它指向的值 没有其余开销 最常见的指针类型 智能指针是这样一些数据结构: 行为和指针相似 有额外的元数据和功能 通过记录所有者的数量,使一份数据被多个

    2023年04月16日
    浏览(53)
  • Rust编程语言入门之无畏并发

    Concurrent:程序的不同部分之间独立的执行(并发) Parallel:程序的不同部分同时运行(并行) Rust无畏并发:允许你编写没有细微Bug的代码,并在不引入新Bug的情况下易于重构 注意:本文中的”并发“泛指 concurrent 和 parallel 在大部分OS里,代码运行在进程(process)中,OS同时

    2023年04月19日
    浏览(70)
  • 【学习笔记】C#基础 - 由C/C++衍生出来的面向对象的编程语言

    1、基本语法 2、类的命名 必须以 A-Z / _ / @ 开头,不能是数字,之后可以跟 A-Z 、 0-9 、 _ 、 @ 不能包含任何空格或特殊符号,比如 ?-+!#%^*()[]{}.;:\\\"\\\'/ 不能与同名(除非添加 @ 前缀,@不作为标识符本身的一部分),不能与类库同名 必须区分大小写(PascalCase命名法) 3、关

    2024年02月07日
    浏览(55)
  • Rust编程语言入门之cargo、crates.io

    通过 release profile 来自定义构建 在https://crates.io/上发布库 通过 workspaces 组织大工程 从 https://crates.io/来安装库 使用自定义命令扩展 cargo release profile: 是预定义的 可自定义:可使用不同的配置,对代码编译拥有更多的控制 每个 profile 的配置都独立于其它的 profile cargo 主要的

    2023年04月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包