RUST博客帖子编辑示例

状态模式(state pattern)是一种面向对象的设计,它的关键点在于:一个值拥有的内部状态由数个状态对象(state object)表的而成,而值的行为则随着内部状态的改变而改变。

下面的示例用来实现发布博客的工作流程:

  1. 在新建博客时生成一个空白的草稿文档,状态是Draft
  2. 在草稿编辑完毕后,请求对这个文章的状态进行审批(request_review),文档此时状态切换成了PendingReview
  3. 文章通过审批后对外正式发布,状态为Published
  4. 仅返回并打印成功发布后的文章,其他状态的文章都应该是对外不可见的

State trait定义了文章状态共享的行为,状态DraftPendReviewPublished都会实现State trait

State traitrequest_review声明中,第一个参数的类型是self: Box<Self>,而不是self&self&mut self。这个语法意味着该方法只能被包裹着当前实例的Box调用,它会在调用过程中获取Box<Self>的所有权并使旧的状态失效,从而将Post状态转换为一个新的状态。

// lib.rs
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;
}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())}}
}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}fn content<'a>(&self, post: &'a Post) -> &'a str {""}
}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 {})}fn content<'a>(&self, post: &'a Post) -> &'a str {""}
}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}
}

request_review

为了消耗旧的状态,request_review方法需要获取状态值的所有权。这也正是Poststate字段引入Option的原因:RUST不允许结构体中出现未被填充的值。我们可以通过Option<T>take方法来取出state字段的Some值,并在原来的位置留下一个None

我们需要临时把state设置为None来取得state值的所有权,而不能直接使用self.state = self.state.request_review()这种代码,这可以确保Post无法在我们完成状态转换后再次使用旧的state值。

take方法的作用:Takes the value out of the option, leaving a [None] in its place.

content

content方法体中调用了Optionas_ref方法,因为我们需要的只是Option中值的引用,而不是它的所有权。由于state的类型是Option<Box<dyn State>>,所以我们在调用as_ref时得到Option<&Box<dyn State>>。如果这段代码中没有调用as_ref,就会导致编译错误,因为我们不能将state从函数参数的借用&self中移出。

我们需要在这个方法上添加相关的声明周期标注,该方法接受post的引用作为参数,并返回post中的content作为结果,因此,该方法中返回值的声明周期应该与post参数的声明周期相关。

as_ref方法声明:Converts from &Option<T> to Option<&T>,但例子中属于Option<T>Option<&T>的转换

代码冗余

示例的代码存在一个缺点:DraftPendReviewPublished重复实现了一些代码逻辑。你也许会试着提供默认实现,让State traitrequest_reviewapprove方法默认返回self。但这样的代码违背了对象安全规则,因为trait无法确定self的具体类型究竟是什么。如果我们希望将State作为trait对象来使用,那么它的方法就必须全部是对象安全的。

use

代码中使用了main.rslib.rs两个文件,在lib.rs也没有做任何mod的声明。在main.rs中通过使用blog::Post路径进行导,而不是crate::Post

根路径blog和我们配置文件Cargo.tomlpackage.name的声明有关系,根路径直接使用了包的名字。

// main.rs
use blog::Post;fn main() {let mut post = Post::new();post.add_text("l go out to play");assert_eq!("", post.content());post.request_review();assert_eq!("", post.content());post.approve();assert_eq!("l go out to play", post.content());
}

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

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

相关文章

Leetcode—231.2的幂【简单】

2023每日刷题&#xff08;五十四&#xff09; Leetcode—231.2的幂 实现代码 class Solution { public:bool isPowerOfTwo(int n) {if(n < 0) {return false;}long long ans 1;while(ans < n) {ans * 2;}if(ans n) {return true;}return false;} };运行结果 之后我会…

时间序列预测专栏介绍 — 算法原理、源码解析、项目实战

专栏链接&#xff1a;https://blog.csdn.net/qq_41921826/category_12495091.html 专栏内容 所有文章提供源代码、数据集、效果可视化 文章多次上热搜榜单 时间序列预测存在的问题 现有的大量方法没有真正的预测未来值&#xff0c;只是用历史数据做验证 利用时间序列分解算法存…

【Vue第3章】使用Vue脚手架_Vue2

目录 3.1 初始化脚手架 3.1.1 说明 3.1.2 具体步骤 3.1.3 模板项目的结构 3.1.4 笔记与代码 3.1.4.1 笔记 3.1.4.2 01_src_分析脚手架 3.2 ref与props 3.2.1 ref 3.2.2 props 3.2.3 笔记与代码 3.2.3.1 笔记 3.2.3.2 02_src_ref属性 3.2.3.3 03_src_props配置 3…

根据应聘者的姓名和所学专业判断是否需要这样的程序设计人员

一、程序分析 导入Scanner函数&#xff0c;分别输入应聘者的姓名和应聘者所学的程序设计语言。 二、具体代码 import java.util.Scanner; public class Recruitment {public static void main(String[] args){try (Scanner scan new Scanner(System.in)) {System.out.prin…

Spring Boot 3 整合 Mybatis-Plus 实现动态数据源切换实战

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

1-5、JDK API文档

语雀原文链接 文章目录 jdk1.8中文.CHM JDK8中文在线文档&#xff1a;https://www.matools.com/api/java8Java11中文在线文档&#xff1a;https://www.matools.com/api/java11

CEEMDAN-Transformer时间序列预测实战完整代码数据可直接运行

项目视频讲解: CEEMDAN-Transformer时间序列预测实战完整代码数据_哔哩哔哩_bilibili 完整代码: import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler import torch import to…

ROS-log功能区别

ROS使用rosout包来记录各个节点的log信息&#xff0c;通常这些log信息是一些可以读懂的字符串信息&#xff0c;这些信息一般用来记录节点的运行状态。 ROS有五种不同类型的log信息&#xff0c;分别为&#xff1a;logdebug、loginfo、logwarn、logerr、logfatal。 等级由低到高&…

GO设计模式——6、原型模式(创建型)

目录 原型模式&#xff08;Prototype Pattern&#xff09; 优缺点 使用场景 注意事项 代码实现 原型模式&#xff08;Prototype Pattern&#xff09; 原型模式&#xff08;Prototype Pattern&#xff09;是用于创建重复的对象&#xff0c;同时又能保证性能。这种类型的设计…

m_map导入本地地形数据

m_map绘制地形图时&#xff0c;虽然自带有1的地形图以及从NOAA下载的1分的地形图&#xff08;详见&#xff1a;Matlab下地形图绘图包m_map安装与使用&#xff09;&#xff0c;但有时需要对地形图分辨率的要求更高&#xff0c;便无法满足。 此时&#xff0c;需要导入本地地形数…

算法Day22 星南二楼(最长升序子序列)

星南二楼&#xff08;最长升序子序列&#xff09; Description Input Output Sample 代码 import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int[] grid new int[n];for(int j0;j&l…

selenium火狐避免被检测向ChartGPT的有效提问

问题描述 当我们问 ChartGPT 如果解决 selenium 被屏蔽问题的时候&#xff0c;它总是回答解决问题的方向&#xff0c;没有给出具体怎么用代码实现。 问题原因 1、可能是描述不清晰 没有告诉GPT使用什么编程语言&#xff0c;没有说明使用火狐还是谷歌浏览器&#xff0c;没有…

深入解析NK模型:复杂适应性系统的演化之谜

NK模型是一种用于研究复杂适应性系统的数学模型&#xff0c;最初由生物学家 Stuart Kauffman 于1993年提出。这模型的目的是模拟生物进化过程中的复杂性&#xff0c;并通过网络结构和相互作用来研究解空间的性质。 目录 一、NK模型介绍1. 模型基础2. 模型参数3. 适应性函数4..…

ubuntu里安装docker

1、更新软件包 在终端中执行以下命令来更新Ubuntu软件包列表和已安装软件的版本: sudo apt update sudo apt upgrade 2、安装docker依赖 Docker在Ubuntu上依赖一些软件包。执行以下命令来安装这些依赖: apt-get install ca-certificates curl gnupg lsb-release 3、添加Do…

postman接口自动化测试

Postman除了前面介绍的一些功能&#xff0c;还有其他一些小功能在日常接口测试或许用得上。今天&#xff0c;我们就来盘点一下&#xff0c;如下所示&#xff1a; 1.数据驱动     想要批量执行接口用例&#xff0c;我们一般会将对应的接口用例放在同一个Collection中&#xf…

unity 2d 入门 飞翔小鸟 Cinemachine 镜头跟随小鸟 多边形碰撞器 解决镜头不会穿模问题(十二)

1、安装 window->package manager 2、创建Cinemachine 右键->Cinemachine->2D Carmera 3、创建空对象和多边形控制器如图 记得勾选 is Trigger 空对象位置记得要和小鸟保持一致&#xff0c;不然等下写完脚本后&#xff0c;镜头一开始会移动一下 4、将多边形触…

代码随想录算法训练营第四十天|139.单词拆分,多重背包,背包问题

139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 示例 1&a…

【Delphi】FMX开发 ios 和 android 异同点(踩坑记)

目录 一、前言 二、补充下基础知识 1. APP程序事件&#xff1a;TApplicationEvent 2. APP内置Web服务器或者UDP服务端或者TCP服务端 三、iOS 和 android 平台的不同点 1. TApplicationEvent的不同点&#xff1a;以下不同点&#xff0c;请仔细阅读&#xff01; 2. APP内置…

AI 绘画 | Stable Diffusion 人物换脸

前言 这篇文章教会你如何使用Stable Diffusion WEB UI扩展插件ReActor轻松实现图片中的人物换脸。ReActor 是 Stable Diffusion WebUI 的扩展,它允许在图像中非常简单准确地进行人脸替换(人脸交换)。 安装环境准备 安装 Visual Studio 2022(例如,社区版本 - 需要此步骤来…

十八、FreeRTOS之FreeRTOS任务通知

本节需要掌握以下内容&#xff1a; 1、任务通知的简介&#xff08;了解&#xff09; 2、任务通知值和通知状态&#xff08;熟悉&#xff09; 3、任务通知相关API函数介绍&#xff08;熟悉&#xff09; 4、任务通知模拟信号量实验&#xff08;掌握&#xff09; 5、任务通知…