一个例子给你讲述 rust 为什么放弃了 class

947 阅读3分钟

在我刚开始学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 上的指定目录,如果稍作改进的话,还可以实现文件的并行上传...

代码来源 oss-rs/file.rs at master · tu6ge/oss-rs (github.com)