rust 如何实现一个函数的不同向量的结构与特质?

3z6pesqy  于 2022-12-04  发布在  其他
关注(0)|答案(2)|浏览(136)

我有3个Vec<T>结构体共享相同的函数:

Vec<RawTransaction>
Vec<RawCashTransaction>
Vec<RawAdjustmentTransaction>

这三个函数都有相同的VerifyableRaw特性和verify()函数,我使用verify()函数检查数组/向量内容的有效性。
下面是我的实现。正如您所看到的,它们都共享相同的基本字段,即:datetotalcreditdebit中的一个或多个。
我的问题是:因为我使用了这些结构体共享的相同字段,所以verify()函数对所有这些结构体都是相同的。在verify函数中,我需要访问datetotalcreditdebit字段,所以我只是将代码从一个实现复制并粘贴到另一个实现中。
我的问题是:我可以将这个trait实现重构为一个函数定义吗?
我发现每次需要对另一个结构体使用verify()函数和VerifyableRaw特征时,我都需要重复自己的操作

pub struct RawTransaction {
    pub date: Option<NaiveDate>,
    pub contact_name: String,
    pub total: Option<Decimal>,
    pub credit: String,
    pub debit: String,
}

pub struct RawCashTransaction{
    pub tr_type: String,
    pub date: Option<NaiveDate>,
    pub contact_name: String,
    pub total: Option<Decimal>,
    pub credit: String,
    pub debit: String,
}

pub struct RawAdjustmentTransaction{
    pub date: Option<NaiveDate>,
    pub info: String,
    pub total: Option<Decimal>,
    pub credit: String,
    pub debit: String,
}

下面是我的特征实现:

#[async_trait]
pub trait VerifyableRaw {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err>;
}

#[async_trait]
impl VerifyableRaw for Vec<RawTransaction> {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err> {
        /// .... this function is the same for all three

        let data = &self; // access the vector
        for (i, row) in data.iter().enumerate() {
            // enumerate each item in this vector
            let date = row.date.unwrap(); // check if the date is valid, etc
            let de = row.debit.clone(); // check if this value is valid
            let cr = row.credit.clone(); // check if this value is valid
            //  ... another process here ...
        }
    }
}

#[async_trait]
impl VerifyableRaw for Vec<RawCashTransaction> {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err> {
        /// .... this function is exactly the same as RawTransaction above
    }
}

#[async_trait]
impl VerifyableRaw for Vec<RawAdjustmentTransaction> {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err> {
        /// .... this function is exactly the same as RawTransaction above
    }
}
unftdfkk

unftdfkk1#

为了避免重复,可以将公共字段放在一个结构体中,而不是将每个字段都复制到每个具体结构体中,只需包含一个类型为公共类型的字段:

pub struct BaseTransaction {
    pub date: Option<NaiveDate>,
    pub total: Option<Decimal>,
    pub credit: String,
    pub debit: String,
}

pub struct RawTransaction {
    pub base: BaseTransaction,
    pub contact_name: String,
}

pub struct RawCashTransaction{
    pub base: BaseTransaction,
    pub tr_type: String,
    pub contact_name: String,
}

pub struct RawAdjustmentTransaction{
    pub base: BaseTransaction,
    pub info: String,
}

impl AsRef<BaseTransaction>为每种混凝土类型:

impl AsRef<BaseTransaction> for RawTransaction {
    fn as_ref (&self) -> &BaseTransaction {
        &self.base
    }
}
impl AsRef<BaseTransaction> for RawCashTransaction {
    fn as_ref (&self) -> &BaseTransaction {
        &self.base
    }
}
impl AsRef<BaseTransaction> for RawAdjustmentTransaction {
    fn as_ref (&self) -> &BaseTransaction {
        &self.base
    }
}

现在你可以有一个通用函数来操作AsRef<BaseTransaction>

impl<T: AsRef<BaseTransaction>> VerifyableRaw for Vec<T> {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err> {
        let base: BaseTransaction = self.as_ref();
        unimplemented!()
    }
}
ccgok5k5

ccgok5k52#

是的,您可以:

pub trait VerifyableRaw {
    fn date(&self) -> Option<NativeDate>;
    fn credit(&self) -> &str;
    fn debit(&self) -> &str;
}

pub trait VerifyableRaws {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err>;
}

impl VerifyableRaw for RawCashTransaction {
  fn date(&self) -> Option<NativeDate> { self.date }
  fn credit(&self) -> &str { &self.credit }
  fn debit(&self) -> &str { &self.debit }
}

impl<T: VerifyableRaw> VerifyableRaws for Vec<T> {
    async fn verify(&self, cid: String, db: Database) -> Result<bool, Err> {
        // your implementation here, but replace field access with method calls
    }
}

// other types have the same implementation

您仍然必须为每个类型的访问方法编写实现,但我相信这比复制复杂的业务逻辑要好。
如果你有太多这样的类型,而你又不想为每一个类型手工编写getter,那么编写一个宏来为你做这件事是很简单的。但是对于仅仅3个类型,我不推荐这样做,因为宏会使事情变得比它们需要的复杂得多。

相关问题