Search code examples
rustmacrosargumentsimplementation

How to make macros that would implement different of args to structure baaed on given tuple?


I tried to make structure that contains tuple that has different size. Structure implements function get that would return tuple created as shown in example.

// Passed tuple with four elements into macros
impl <A0, A1, A2, A3> Cont<(A0, A1, A2, A3)> {
    fn get(&self) -> (A0, A1, A2, A3) {
        (self.data.0, self.data.1, self.data.2, self.data.3)
    }
}
// Passed tuple with five elements into macros
impl <A0, A1, A2, A3, A4> Cont<(A0, A1, A2, A3, A4)> {
    fn get(&self) -> (A0, A1, A2, A3, A4) {
        (self.data.0, self.data.1, self.data.2, self.data.3, self.data.4)
    }
}

I'm not sure how to use cycle as A|number|


Solution

  • What you can or should do, is not care about the tuples. One generic parameter can cover all tuples, see this:

    struct Foo<T> {
        data: T,
    }
    
    impl<T> Foo<T> {
        fn get(&self) -> &T {
            &self.data
        }
    }
    
    fn example() {
        let foo = Foo { data: (1, "hello") };
        assert_eq!(foo.get(), &(1, "hello"));
    
        let foo = Foo {
            data: (1, "hello", 2),
        };
        assert_eq!(foo.get(), &(1, "hello", 2));
    }
    

    Edit

    If you want limit what type can hold, you usually do it with traits to keep common functions names.

    macro_rules! impl_tuple {
        ($(
            ($($p:ident),*);
        )*) => {
            $(
                impl<$($p),*> sealed::Sealed for ($($p,)*) {}
            )*
        };
    }
    
    mod sealed {
        pub trait Sealed {}
    }
    
    pub trait ValidData: sealed::Sealed {}
    impl<T: sealed::Sealed> ValidData for T {}
    
    impl_tuple! {
        (A);
        (A, B, C);
        (A, B, C, D, E);
    }
    
    struct Foo<T: ValidData> {
        data: T,
    }
    
    impl<T: ValidData> Foo<T> {
        fn get(&self) -> &T {
            &self.data
        }
    }
    
    fn main() {
        let foo = Foo { data: 1 }; // fails
                                   // the trait bound `{integer}: Sealed` is not satisfied
                                   // the following other types implement trait `Sealed`:
                                   //   (A, B, C)
                                   //   (A, B, C, D, E)
                                   //   (A,) [E0277]
        let foo = Foo { data: (1,) }; // ok
    }