How to implement a function for different vector of structs with traits?

I have 3 Vec<T> structs that shared the same function:

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

All three shared the same VerifyableRaw traits and the verify() function. I use the verify() function check the validity of the content of that array/vector.

Here's my implementation. As you can see, all of them shared the same basic fields, namely: date, total, credit, and debit.

My problem is: since I use the same fields that those structs shared, the verify() function is the same for all of them. In verify function, I need to access the date, total, credit, and debit fields so I just copy and paste the code from one implementation to another.

My question is: Can I refactor this trait implementation into a single function definition ?

I found out that I need to repeat myself each time I need to use verify() function and VerifyableRaw trait to another struct that needs it

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,
}

Here's my trait implementation:

#[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
    }
}

Extract the common fields into a new struct, and forward to its verify() method in each trait impl.

3 Likes