我正在修改我的rust ecs库,我正在重写组件迭代器。
主要的想法是能够通过调用我的迭代宏来迭代任何组件的元组,无论是否可变:
for (pos: &mut Position, vel: &Velocity) in component_iterator!(compnents; mut Position, Velocity) {
Position += Velocity;
}
其中Position
和Velocity 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
1条答案
按热度按时间mf98qq941#
这是由于宏观卫生。宏内部的
self
值不能从外部访问,相反,调用方的self
值不能从宏内部访问。您可以通过在宏参数中捕获self
来解决它:Playground
参见How to call methods on self in macros?