在我刚开始学rust的时候,就想知道答案,但是找了很多地方,大多都是些理论介绍,理解了很久才明白,今天在维护项目的时候,找到了一个例子,很切这个题目,尝试说明一下。
在看代码之前,我们先讲下开始学面向对象时,讲的那个故事,对象是层级关系,通常情况下,对象之间是上下级的关系,例如 "动物“ -> “猫,老鼠”,“人” -> “男人” -> "小明" ......
但是,如果遇到下面这种情况,我们在处理问题的时候,就不那么方便了,例如:
- 汽车: 有引擎,可以跑,可以修轮胎
- 自行车,没有引擎,可以跑,可以修轮胎
- 人:没有引擎,可以跑,不能修轮胎
我们可以发现,人,自行车,汽车,不属于从属关系,但是他们是有一些共同的能力--跑,假如说,我们用面向对象的方式对他们进行表现的话,会面临一些问题,无法对跑这个功能进行抽象,无法统一修轮胎这个工作的步骤,因为无论我们是修汽车,还是自行车的轮胎,我们都需要把轮胎拆下来,修完后,也都需要装回去。
因为无论,汽车,自行车,人,跑的时候,都会有:起点,终点,距离,速度,因为这些对象无法相互继承,所以只能各自实现一套。但是当我们需要将他们组合在一起进行分析的时候,可能就会遇到问题,因为他们各自的跑功能,有的只收集了距离信息,有的只收集了速度信息。
这是两种实现思路,类似于 QQ 和微信的朋友分组功能,QQ是分组,微信是标签,一个好友只能归属于一个分组,但是却可以拥有多个标签。
这就是 rust 中的 Trait 功能的一个思路,rust,golang 等现代语言,将数据和行为进行了分离,让写代码的思路更自由了一些。其中 rust 中的 struct,enum 等代表了数据类型,在这些类型上面,可以跟 trait 进行组合,trait 是包含一组行为的组织,可以继承,也支持泛型。
具体一些,比如阿里云的 OSS 有上传,下载,删除等操作文件的功能。按一般思路讲,在一个 Bucket 上,你是可以对文件进行这些操作的。但是如果作为一个 rust 的 OSS sdk 的话,可能还有别的模块需要这些功能:
Client: 封装 http 请求以及签名功能Bucket: 对应 OSS 上的某一个 bucket 实例ObjectList: 在对 OSS 上文件进行遍历时,保存下来的文件列表
这些 struct 似乎都需要有这些功能, Client 对应默认 endpoint,默认 bucket 上的文件, Bucket ObjectList 对应当前 bucket 上的文件。所以他们都需要有上传等功能。这些功能既然这么好用,能不能更进一步,开放出来,给其他开发者使用呢,答案是肯定的。
这就是一个简单的例子:
use std::{fs, path::Path};
use aliyun_oss_client::{
config::ObjectPath,
file::{File, FileError, Files},
BucketName, Client, EndPoint, KeyId, KeySecret,
};
struct MyObject {
path: ObjectPath,
}
impl MyObject {
const KEY_ID: KeyId = KeyId::from_static("xxxxxxxxxxxxxxxx");
const KEY_SECRET: KeySecret = KeySecret::from_static("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
const END_POINT: EndPoint = EndPoint::CnShanghai;
const BUCKET: BucketName = unsafe { BucketName::from_static2("xxxxxx") };
fn new(path: &Path) -> Result<MyObject, FileError> {
Ok(MyObject {
path: path.try_into()?,
})
}
}
impl File for MyObject {
type Client = Client;
fn get_path(&self) -> ObjectPath {
self.path.clone()
}
fn oss_client(&self) -> Self::Client {
Client::new(
Self::KEY_ID,
Self::KEY_SECRET,
Self::END_POINT,
Self::BUCKET,
)
}
}
#[tokio::main]
async fn main() -> Result<(), FileError> {
for entry in fs::read_dir("examples")? {
let path = entry?.path();
let path = path.as_path();
if !path.is_file() {
continue;
}
let obj = MyObject::new(path)?;
let content = fs::read(path)?;
let res = obj.put_oss(content, Client::DEFAULT_CONTENT_TYPE).await?;
println!("result status: {}", res.status());
}
Ok(())
}
这个例子是在一个简单的 struct 上实现了上传,下载,删除功能,这里假设了你使用的秘钥,可用区,bucket都是固定不变的,那么,将本地文件进行上传,就很简单了。这个例子是将一个目录下的文件都上传到 oss 上的指定目录,如果稍作改进的话,还可以实现文件的并行上传...