rust 宏递归调用参数中的“预期值,找到模块`self`”

s6fujrry  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(104)

我正在修改我的rust ecs库,我正在重写组件迭代器。
主要的想法是能够通过调用我的迭代宏来迭代任何组件的元组,无论是否可变:

for (pos: &mut Position, vel: &Velocity) in component_iterator!(compnents; mut Position, Velocity) {
    Position += Velocity;
}

其中PositionVelocity are structs
我解析宏输入的方法是递归调用,它对输入重新排序,允许我一次生成一个组件的代码。
宏将生成一个结构体,在其上实现Iterator trait并返回其示例。这个问题来自于实现Iterator trait的宏。
警告,大量的代码即将到来,我会尽量保持简短:下面是我解析的四个条件之一:

macro_rules! impl_iterator_inner {
    // we are adding a non terminal non mutable component.
    (
        impl<'a> Iterator for MacroGeneratedComponentIterator<'a> {
            type Item = ($($item_out:tt)*);
            fn next(&mut self) -> Option<Self::Item> {
                loop {
                    match self.current_component {
                        ($($match_out:tt)*)
                        MacroGeneratedComponentsEnum::EndOfIterator => {
                            let result = ($($result_out:tt)*);
                            self.current_entity += 1;
                            self.current_component = macro_generated_reset();
                            return Some(result);
                        }
                    }
                }
            }
        }, $comp:ident, $($rest:tt)+
    ) => {
        paste::paste! {           
            impl_iterator_inner!(
                impl<'a> Iterator for MacroGeneratedComponentIterator<'a> {
                    type Item = ($($item_out)* &'a $comp,);
                    fn next(&mut self) -> Option<Self::Item> {
                        loop {
                            match self.current_component {
                                (
                                    $($match_out)*
                                    MacroGeneratedComponentsEnum::$comp => {
                                        while {
                                            let elem = &self.[<$comp:snake>].peek()?;
                                            if elem.index > self.current_entity {
                                                self.current_entity = elem.index; // update the current entity
                                                self.current_component = macro_generated_reset();
                                                false
                                            } else if elem.index == self.current_entity {
                                                match self.active_entities.get(self.current_entity)? {
                                                    true => {
                                                        self.current_entity += 1; // current entity is inactive, go to next one
                                                        self.current_component = macro_generated_reset();
                                                    }
                                                    false => self.current_component = macro_generated_return_next(self.current_component),
                                                }
                                                false // stop iteration
                                            } else { true /* keep iterating */ }
                                        } {
                                            self.[<$comp:snake>].next();
                                        }
                                    }
                                )
                                MacroGeneratedComponentsEnum::EndOfIterator => {
                                    let result = (
                                        (
                                            $($result_out)*
                                            &self.[<$comp:snake>].next()?.elem,
                                        )
                                    );
                                    self.current_entity += 1;
                                    self.current_component = macro_generated_reset();
                                    return Some(result);
                                }
                            }
                        }
                    }
                }, $($rest)+
            );
        }
    };
}

在参数中,我将使用整个impl,下一个组件和其他组件。在当前组件的基础上,我将只使用其余部分再次调用宏,并将当前组件的代码添加到impl中。我有更多这样的例子,它们处理mut $comp:ident的可变组件,以及终止情况,在这种情况下,我返回整个impl。
我需要这些递归调用,因为我找不到另一种方法来正确处理mut Component输入,代码生成是在不同的地方完成的,宏必须是健康的。
现在,我的问题是:
当我在单个组件上尝试这个方法时,我只阅读terminaison的情况(这与所示的情况非常相似,并且参数中没有rest),它工作得很好。但是当我有一个递归调用的时候,我得到了一个错误:
在我的宏调用中,在self关键字的每个示例处都添加“expected value, found module self”。我以为你可以把任何东西都放在宏调用中,只要它被解析了?
我不明白它,因为我正在生成一个完整的impl,所以&mut self在函数中被正确定义了吗?
这个问题可以在这里更详细地探讨:https://github.com/VirgileHenry/Foundry(警告,大量代码正在进行和重构,因此某些示例无法工作。)
这是怎么回事?
编辑:
下面是一个重现问题的最小示例:我使用递归宏调用来为一个结构体生成一个impl,它抛出了相同的错误:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=369340ee70847e631eef219883938827

mf98qq94

mf98qq941#

这是由于宏观卫生。宏内部的self值不能从外部访问,相反,调用方的self值不能从宏内部访问。您可以通过在宏参数中捕获self来解决它:

macro_rules! impl_macro {
    (
        impl MyTrait for MyStruct {
            fn draw(&mut $self:ident) {
                ($($out:tt)*)
            }
        }, $to_draw:expr, $($rest:tt)+
    ) => {
        impl_macro!(
            impl MyTrait for MyStruct {
                fn draw(&mut $self) {
                    (
                        $($out)*
                        print!("self data : {}", $self.data);
                        println!("{:?}", $to_draw);
                    )
                }
            }, $($rest)+
        );
    };
    (
        impl MyTrait for MyStruct {
            fn draw(&mut $self:ident) {
                ($($out:tt)*)
            }
        }, $to_draw:expr
    ) => {
        impl MyTrait for MyStruct {
            fn draw(&mut $self) {
                $($out)*
                print!("self data : {}", $self.data);
                println!("{:?}", $to_draw);
            }
        }
    };
}

struct MyStruct {
    pub data: u8,
}

trait MyTrait {
    fn draw(&mut self);
}

fn main() {
    impl_macro!(
        impl MyTrait for MyStruct {
            fn draw(&mut self) {
                ()
            }
        }, "first draw", "second", 12
    );

    let mut st = MyStruct { data: 0 };
    st.draw();
}

Playground
参见How to call methods on self in macros?

相关问题