Home > Blockchain >  How to deduplicate Rust functions accepting different structs with common properties?
How to deduplicate Rust functions accepting different structs with common properties?

Time:12-11

I have a couple of structs (StructX and StructY), and another struct that has all the common reference properties of the previous ones (StructCommon).

I also have a function for each of StructX and StructY that returns a StructCommon, but my issue is that I had to write two functions for it.

pub struct StructX<'a> {
    a: &'a str,
    b: &'a str,
    x: &'a str,
}

pub struct StructY<'a> {
    a: &'a str,
    b: &'a str,
    y: &'a str,
}

pub struct StructCommon<'a> {
    a: &'a str,
    b: &'a str,
}

impl<'a> StructCommon<'a> {
    pub fn from_x<T>(serialized: &StructX) -> StructCommon<'a>
    {
        StructCommon {
            a: serialized.a,
            b: serialized.b,
        }
    }

    pub fn from_y<T>(serialized: &StructY) -> StructCommon<'a>
    {
        StructCommon {
            a: serialized.a,
            b: serialized.b,
        }
    }

    // Pseudo-Rust proposed solution example:
    // pub fn from_either<T>(serialized: &StructX | &StructY) -> StructCommon<'a>
    // {
    //     StructCommon {
    //         a: serialized.a,
    //         b: serialized.b,
    //     }
    // }
}

How can I - if possible - deduplicate the from_x() and from_y() methods so I would only have to write the logic for extracting the common properties only once**?**

I have written an imaginary pseudo-code solution example method commented out called from_either().

CodePudding user response:

There is another answer focusing on your specific question, but I propose an alternative solution of having StructX and StructY containing a StructCommon instead of having fields that happen to have the same name and type as those in StructCommmon. This reduces the boilerplate of converting by just returning the internal structure:

pub struct StructX<'a> {
    common: StructCommon<'a>,
    x: &'a str,
}

pub struct StructCommon<'a> {
    a: &'a str,
    b: &'a str,
}

impl<'a> Into<StructCommon<'a>> for StructX<'a> {
    fn into(self) -> StructCommon<'a> {
        self.common
    }
}

This is still some boilerplate, but much less and doesn't need to change when you change the common fields.

CodePudding user response:

I don't think you can directly express what you want using generics in Rust unless you write a trait with methods like get_a() and get_b() and use that for your generic implementation of from_either(). That really doesn't reduce your boilerplate though.

However, procedural macros looks like a good fit for what you're trying to do. For example, you could create a derive macro that implements Into<StructCommon> for StructX and StructY, and the macro can write the boilerplate code for you.

  • Related