rust编程语言入门之rust的面向对象编程特性(代码片段)

小乔的博客 小乔的博客     2023-04-21     658

关键词:

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 代码:

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编程语言入门之函数式语言特性:-迭代器和闭包(代码片段)

函数式语言特性:-迭代器和闭包本章内容闭包(closures)迭代器(iterators)优化改善12章的实例项目讨论闭包和迭代器的运行时性能一、闭包(1)-使用闭包创建抽象行为什么是闭包(closure)闭包:可以捕获其所在环境的匿名函数... 查看详情

rust语言特性之变量

...f1a;学习RUST语言。这几天我把RUST语法过了一遍。有了其它编程语言的基础,RUST语法学起来不难。但RUST毕竟是一门全新设计的语言,如果和现有语言完全一样,那就失去了存在的价值。RUST作为一门年轻的语言,博... 查看详情

rust编程语言入门之编写自动化测试(代码片段)

编写自动化测试一、编写和运行测试测试(函数)测试:函数验证非测试代码的功能是否和预期一致测试函数体(通常)执行的3个操作:准备数据/状态运行被测试的代码断言(Assert)结果解剖测试函数测试函数需要使用test属性... 查看详情

rust编程语言入门(代码片段)

Rust编程语言入门Rust简介为什么要用Rust?Rust是一种令人兴奋的新编程语言,它可以让每个人编写可靠且高效的软件。它可以用来替换C/C++,Rust和他们具有同样的性能,但是很多常见的bug在编译时就可以被消灭。Rust是一种通用的... 查看详情

rust编程语言入门之项目实例:-命令行程序(代码片段)

项目实例:-命令行程序一、实例:接收命令行参数本章内容12.1接收命令行参数12.2读取文件12.3重构:改进模块和错误处理12.4使用TDD(测试驱动开发)开发库功能12.5使用环境变量12.6将错误消息写入标准错误而不是标准输出创建... 查看详情

rust编程语言入门之无畏并发(代码片段)

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

rust语言特性之变量

...f1a;学习RUST语言。这几天我把RUST语法过了一遍。有了其它编程语言的基础,RUST语法学起来不难。但RUST毕竟是一门全新设计的语言,如果和现有语言完全一样,那就失去了存在的价值。RUST作为一门年轻的语言,博... 查看详情

rust编程语言入门之最后的项目:多线程web服务器(代码片段)

最后的项目:多线程Web服务器构建多线程Web服务器在socket上监听TCP连接解析少量的HTTP请求创建一个合适的HTTP响应使用线程池改进服务器的吞吐量优雅的停机和清理注意:并不是最佳实践创建项目~/rust➜cargonewhelloCreatedbinary(applica... 查看详情

rust编程语言入门之cargocrates.io(代码片段)

cargo、crates.io本章内容通过releaseprofile来自定义构建在https://crates.io/上发布库通过workspaces组织大工程从https://crates.io/来安装库使用自定义命令扩展cargo一、通过releaseprofile来自定义构建releaseprofile(发布配置)releaseprofile:是预定... 查看详情

rust编程语言入门之模式匹配(代码片段)

模式匹配模式模式是Rust中的一种特殊语法,用于匹配复杂和简单类型的结构将模式与匹配表达式和其他构造结合使用,可以更好地控制程序的控制流模式由以下元素(的一些组合)组成:字面值解构的数组、enum、struct和tuple变... 查看详情

idea搭建rust开发环境,解决不识别rust工程的解决办法(代码片段)

...,实用",支持函数式,并发式,过程式以及面向对象的编程风格。Rust插件的主要特性如下:导航特性:GotoClass、GotoSymbol、GotoSuperModule、Structure、GotoDefinition。编辑器特性 查看详情

深入浅出rust异步编程之tokio(代码片段)

深入浅出Rust异步编程之Tokio本文以tokio为例简单介绍Rust异步编程相关的一些知识。首先让我们看看为什么使用rust来进行异步编程。这里tokio官方给出了一个性能测试的对比,可以看到tokio是性能最好,实际上运行这个基准测试的... 查看详情

rust编程语言入门之泛型trait生命周期(代码片段)

泛型、Trait、生命周期一、提取函数消除重复fnmain()letnumber_list=vec![34,50,25,100,65];letmutlargest=number_list[0];fornumberinnumber_listifnumber>largestlargest=number;println!("Thelargestnumberis",largest);重复代码重复代码的危害:容易出错需求变更时需要在... 查看详情

rust编程语言入门之智能指针(代码片段)

智能指针智能指针(序)相关的概念指针:一个变量在内存中包含的是一个地址(指向其它数据)Rust中最常见的指针就是”引用“引用:使用&借用它指向的值没有其余开销最常见的指针类型智能指针智能指针是这样一些数据... 查看详情

scala编程入门---面向对象编程之trait高级知识

trait调用链Scala中支持让类继承多个Trait后,依次调用多个Trait中的同一个方法,只要让多个trait的同一个方法中,在最后都执行super.方法即可类中调用多个trait中都有这个方法时,首先会从最右边的trait的方法开始执行,然后依次... 查看详情

rust语言特性之所有权

新年开工,开启work&study模式,接着来学习RUST语言。作为一名C/C++程序员,C/C++语言中的指针是使用得最爽的,几乎无所不能,各种奇技淫巧也层出不穷。但C/C++语言中最折磨人的也是指针&#... 查看详情

rust极简教程(代码片段)

...用第三方库异常处理泛型泛型概念特性(接口)文件和IO面向对象并发编程线程消息传递互斥锁代码说明引 查看详情

rust编程指南02-进入rust语言世界(代码片段)

...文链接:https://wayto.rs/about-book.html 欢迎大家加入Rust编程学院,中国最好的Rust学习社区官网:https://college.rsQQ群:1009730433进入Rust编程世界一、Rust发展历程Rust最早是Mozilla雇员GraydonHoare的一个个人项目,从2009年... 查看详情