前言
软件开发的整个行为,基本上可以说是不断创建和迭代接口,然后在这些接口上进行实现的过程
正式开始
用 trait 让代码自然舒服好用
- 在设计 trait 的时候,除了关注功能,还要注意是否好用、易用
- trait 在设计结束之后,不要先着急撰写实现 trait 的代码,而是最好先写一些对于 trait 使用的测试代码
用 trait 做桥接
在 Rust 里,桥接的工作可以通过函数来完成,但最好通过 trait 来桥接
// Engine trait:未来可以添加更多的 engine,主流程只需要替换 engine
pub trait Engine {
// 生成一个新的 engine
fn create<T>(data: T) -> Result<Self>
where
Self: Sized,
T: TryInto<Self>,
{
data.try_into()
.map_err(|_| anyhow!("failed to create engine"))
}
// 对 engine 按照 specs 进行一系列有序的处理
fn apply(&mut self, specs: &[Spec]);
// 从 engine 中生成目标图片,注意这里用的是 self,而非 self 的引用
fn generate(self, format: ImageOutputFormat) -> Vec<u8>;
}
/*
通过 Engine 这个 trait,我们把第三方的库 photon 和自己设计的 Image Spec 连接起来,使得我们不用关心 Engine 背后究竟是什么,只需要调用 apply 和 generate 方法即可
*/
// 使用 image engine 处理
let mut engine = Photon::create(data)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
engine.apply(&spec.specs);
let image = engine.generate(ImageOutputFormat::Jpeg(85));
使用 trait 提供控制反转
通过使用 trait,我们可以在设计底层库的时候告诉上层:我需要某个满足 trait X 的数据,因为我依赖这个数据实现的 trait X 方法来完成某些功能,但这个数据具体怎么实现,我不知道,也不关心
控制反转是架构中经常使用到的功能,它能够让调用者和被调用者之间的关系在某个时刻调转过来,被调用者反过来调用调用者提供的能力,二者协同完成一些事情
用 trait 实现 SOLID 原则
- SRP:单一职责原则,是指每个模块应该只负责单一的功能,不应该让多个功能耦合在一起,而是应该将其组合在一起。
- OCP:开闭原则,是指软件系统应该对修改关闭,而对扩展开放。
- LSP:里氏替换原则,是指如果组件可替换,那么这些可替换的组件应该遵守相同的约束,或者说接口。
- ISP:接口隔离原则,是指使用者只需要知道他们感兴趣的方法,而不该被迫了解和使用对他们来说无用的方法或者功能。
- DIP:依赖反转原则,是指某些场合下底层代码应该依赖高层代码,而非高层代码去依赖底层代码。
资料链接
加餐|Rust2021版次问世了!
升级Rust edition到2021
- 执行rustup update stable 完成工具链的升级
- cargo fix --edition
- 修改 Cargo.toml,替换 edition = “2021”
- cargo build / cargo test 确保一切正常
Rust如何通过版次解决版本兼容问题
- 库的作者还是以旧的版次发布他的代码,使用库的开发者可以选择他们想使用最新的版次,二者可以完全不一致
- 编译时,Rust 编译器以旧的版次的功能编译旧的库,而以新的版次编译使用者的代码
Rust 2021 包括了什么新东西?
闭包的不相交捕获
在 edition 2021 之前,哪怕你只用到了其中一个域,闭包也需要捕获整个数据结构,即使是引用。但是 2021 之后,闭包可以只捕获需要的域
feature resolver
Cargo 的默认行为是在依赖中多次引用单个包时合并所用到的功能
新的prelude
在 2021 版次中,TryInto、TryFrom 和 FromIterator 默认被引入到 prelude 中