Variable 'X' is still repeating at this depth when pass strings sequence to macro

I have a macro and I want to pass sequence of strings into it, but I got an error:

error: variable 'dynamic_field' is still repeating at this depth
   --> src/main.rs:140:41
    |
140 |                     dynamic_fields.push($dynamic_field);
    |                                         ^^^^^^^^^^^^^^

This is my macro:

#[macro_export]
macro_rules! packet {
    (
        $(@option[login_opcode=$login_opcode_value:expr])?
        $(@option[world_opcode=$world_opcode_value:expr])?
        $(@option[compressed:$compressed_value:expr])?
        $(@option[dynamic_fields:$($dynamic_field:expr),*])?

        $(#[$outer:meta])*
        $vis:vis struct $PacketStruct:ident {
            $($field_vis:vis $field_name:ident: $field_type:ty),*$(,)?
        }

        $($PacketStructImpl: item)*
    ) => {
        $(#[$outer])*
        #[derive(Clone, Debug, Default)]
        $vis struct $PacketStruct {
            $($field_vis $field_name: $field_type),*
        }

        $($PacketStructImpl)*

        impl $PacketStruct {
            #[allow(dead_code)]
            pub fn from_binary(buffer: &Vec<u8>) -> Self {
                #![allow(unused_mut)]
                #![allow(unused_variables)]
                #![allow(unused_assignments)]
                let mut omit_bytes: usize = INCOMING_HEADER_LENGTH;
                $(
                    omit_bytes = $login_opcode_value.to_le_bytes().len();
                )?
                $(
                    if $compressed_value {
                        // 4 bytes uncompressed + 2 bytes used by zlib
                        omit_bytes += 6;
                    }
                )?

                let mut internal_buffer: Vec<u8> = Vec::new();
                $(
                    if $compressed_value {
                        let data = &buffer[omit_bytes..];
                        let mut decoder = flate2::read::DeflateDecoder::new(data);
                        std::io::Read::read_to_end(&mut decoder, &mut internal_buffer).unwrap();
                    }
                )?

                let buffer = if internal_buffer.is_empty() {
                    buffer[omit_bytes..].to_vec()
                } else {
                    internal_buffer
                };

                let mut dynamic_fields: Vec<&str> = vec![];
                $(
                    dynamic_fields.push($dynamic_field);
                )*

                let mut reader = std::io::Cursor::new(&buffer);

                let initial = Self {
                    $(
                        $field_name: BinaryConverter::read_from(
                            &mut reader
                        ).unwrap()
                    ),*
                };

                initial
            }

            #[allow(dead_code)]
            pub fn to_binary(&mut self) -> Vec<u8> {
                #![allow(unused_mut)]
                let mut body = Vec::new();
                $(
                    BinaryConverter::write_into(
                        &mut self.$field_name,
                        &mut body
                    ).unwrap();
                )*
                let header = Self::_build_header(&body);
                [header, body].concat()
            }

            #[allow(unused_variables)]
            fn _build_header(body: &Vec<u8>) -> Vec<u8> {
                #![allow(unused_mut)]
                let mut header: Vec<u8> = Vec::new();
                $(
                    byteorder::WriteBytesExt::write_u8(
                        &mut header,
                        $login_opcode_value as u8
                    ).unwrap();
                )?
                $(
                    let size = body.len() + OUTCOMING_OPCODE_LENGTH;

                    byteorder::WriteBytesExt::write_u16::<byteorder::BigEndian>(
                        &mut header,
                        size as u16,
                    ).unwrap();
                    byteorder::WriteBytesExt::write_u32::<byteorder::LittleEndian>(
                        &mut header,
                        $world_opcode_value as u32
                    ).unwrap();
                )?

                header
           }

            $(
                #[allow(dead_code)]
                pub fn unpack(&mut self) -> PacketOutcome {
                    ($login_opcode_value as u32, self.to_binary())
                }
            )?

            $(
                #[allow(dead_code)]
                pub fn unpack(&mut self) -> PacketOutcome {
                    ($world_opcode_value as u32, self.to_binary())
                }
            )?
        }
    };
}

This is how I want to use it:

packet! {
    @option[world_opcode=100]
    @option[dynamic_fields:["nickname"]]
    struct Income {
        name: String,
        age: u64,
        nickname: String,
    }
}

This is sandbox to reproduce the problem.

Could somebody explain why this issue happen here and how to fix it ?

Macro variables can only be referred to with the same number of repetition groups as they were defined with. You have an optional group on the outside that isn't present in your use of that macro variable

Something like this should work

$($(dynamic_fields.push($dynamic_field);)*)?

But it looks like there may be some unrelated issues still

3 Likes

Could you clarify what kind of issues do you mean ?


Regarding the code, next works for me:

let mut dynamic_fields: Vec<&str> = vec![];
$(
	$(
		for index in 0..$dynamic_fields.len() {
			dynamic_fields.push($dynamic_fields[index]);
		}
	)*
)?

because I pass sequence, it detected as [&str; N].

Thank you @semicoleon for explanation !

1 Like

Ahh I see, I was missing that it was an array of strings. You should be good then!

2 Likes