Notification texts go here Contact Us Buy Now!

Tuple indexing in macro

No, tuple indexing is not possible within a declarative macro. The expression $idx + 1 will produce distinct tokens, like 0, +, and 1, and there is no way to evaluate that to a single literal token.

Procedural macros, however, can easily handle this task. You can use the typle crate to generate code that works with tuples of any length:

    
#[typle(Tuple for 0..=12)]
impl Packer for Iproto
where
    T: Tuple,
    typle_bound!(i in .. => T<{i}>): Pack<T<{i}>>,
{
    fn pack(self, data: T) -> Vec<u8> {
        let mut out = Vec::new();
        for typle_index!(i) in 0..T::LEN {
            out.append(&mut T::<{i}>::pack(data[[i]]));
        }
        out
    }
}
    
  

This generates code equivalent to the following for 0..12 components:

    
impl Packer<(T0, T1)> for Iproto
where
    T0: Pack,
    T1: Pack,
{
    fn pack(self, data: (T0, T1)) -> Vec<u8> {
        let mut out = Vec::new();
        out.append(&mut <T0>::pack(data.0));
        out.append(&mut <T1>::pack(data.1));
        out
    }
}
    
  

Another approach with declarative macros involves using destructuring, which allows you to access tuple elements by their names instead of indices:

    
macro_rules! gen_packer {
    ($($T:ident),+) => {
        impl<$($T,)+> Packer<($($T,)+)> for Iproto
        where $(Iproto: Packer<$T>,)+
        {
            fn pack(&mut self, data: ($($T,)+)) -> Vec<u8> {
                #[allow(non_snake_case)]
                let ( $($T,)+ ) = data;
                let mut out = vec![];
                $(
                    out.append(&mut self.pack($T));
                )*
                out
            }
        }
    }
}

pub struct Iproto { }
pub trait Packer<T> {
    fn pack(&mut self, arg: T) -> Vec<u8>;
}

gen_packer!(A);
gen_packer!(A, B);
// ...
    
  

Finally, you can use a recursive macro to generate the code for tuples of any length:

    
macro_rules! gen_packer {
    () => {
        impl Packer<()> for Iproto {
            fn pack(&mut self, _data: ()) -> Vec<u8> {
                vec![]
            }
        }
    };

    ($first:ident $(, $rest:ident)*) => {
        impl<$first $(, $rest)*> Packer<($first, $($rest,)*)> for Iproto
        where
            Iproto: Packer<$first>,
            $(Iproto: Packer<$rest>,)*
        {
            fn pack(&mut self, data: ($first, $($rest,)*)) -> Vec<u8> {
                #[allow(non_snake_case)]
                let ( $first, $($rest,)* ) = data;
                let mut out = vec![];
                out.append(&mut self.pack($first));
                $(
                    out.append(&mut self.pack($rest));
                )*
                out
            }
        }

        gen_packer!($($rest),*);
    };
}

pub struct Iproto { }
pub trait Packer<T> {
    fn pack(&mut self, arg: T) -> Vec<u8>;
}

gen_packer!(A, B, C, D, E, F, G, H, I, J, K);
    
  

Post a Comment

Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.