Macros 如何在宏中包装do-while样式循环,保持';继续';流量控制?
在Rust中,可以编写do-while样式循环:Macros 如何在宏中包装do-while样式循环,保持';继续';流量控制?,macros,rust,Macros,Rust,在Rust中,可以编写do-while样式循环: loop { something(); if !test() { break; } } 请注意,使用do while表单而不是while test(){something()},其目的是test()可能需要在something()之后运行 这是可行的,但是当逻辑被包装在宏中时,使用continue时发生的情况就不那么明显了。它将跳过测试,可能会进入无限循环: macro_rules! loop_over
loop {
something();
if !test() {
break;
}
}
请注意,使用do while表单而不是while test(){something()}
,其目的是test()
可能需要在something()
之后运行
这是可行的,但是当逻辑被包装在宏中时,使用continue
时发生的情况就不那么明显了。它将跳过测试,可能会进入无限循环:
macro_rules! loop_over_items {
($item:expr, $iter:ident, $code:block) => {
{
let first = $item.first;
let mut $iter = first;
loop {
$code
$iter = $iter.next;
if (first != $iter) {
break;
}
}
}
}
}
这在基本情况下有效:
loop_over_items!(item, iter_elem, {
code_to_run;
});
但这可能会进入一个无限循环,乍一看并不十分明显:
loop_over_items!(item, iter_elem, {
if some_test() {
continue;
}
code_to_run;
});
在Rust中,编写一个支持在
$code
之后跳过逻辑的continue
宏的好方法是什么?添加此答案以显示可以在未知代码块中检测到continue/break,但这并不漂亮
注意,我很想知道使用这种方法的一些影响:
- 如果未在传递到宏的
正文中使用中断/继续检查,则会对其进行优化(这应该是可能的)
- 当检测中断/继续的逻辑应用时,它是否与在while循环中使用continue/break一样有效(do while样式意味着它将不完全相同)
- 当代码块中未使用break/continue时,这些检查将完全针对发布版本进行优化,因此可以在不增加不必要开销的情况下使用此宏
break
或continue
时执行循环迭代的逻辑本身可以封装在宏中:
enum LoopIteration {
Normal,
Break,
Continue,
}
macro_rules! exec_iteration {
($code: block) => {{
let mut _state = LoopIteration::Normal;
loop {
if let LoopIteration::Break = _state {
// got back here after having preparing for break - it
// means a continue happened
_state = LoopIteration::Continue;
break;
}
// prepare for break
_state = LoopIteration::Break;
$code;
// neither break nor continue occurred
_state = LoopIteration::Normal;
break;
}
_state
}}
}
有了它,支持break
和continue
的常规do-while宏可以编写如下:
macro_rules! do_while {
($code: block, $test: expr) => {{
loop {
match exec_iteration!($code) {
LoopIteration::Normal => (),
LoopIteration::Break => break,
LoopIteration::Continue => continue,
}
if !$test {
break;
}
}
}}
}
上面的内容不是很有用,因为我们可以简单地将$code
嵌入到循环中,并具有相同的效果。但是,当在项目上实现类似于循环的宏时,现在有一种方法可以移动到$code
之后的下一个项目,即使它使用continue
:
macro_rules! loop_over_items {
($item:expr, $iter:ident, $code:block) => {{
let first = $item.first;
let mut $iter = first;
loop {
if let LoopIteration::Break = exec_iteration!($code) {
break
}
// Advance the iterator before proceeding, even if
// "continue" was used
$iter = $iter.next;
if first != $iter {
break;
}
}
}}
}
好主意。我已经发布了一个答案,将不漂亮的测试提取到一个单独的宏中,允许对loop\u over\u items
进行合理可读的定义。为什么这个问题被标记为:寻求调试帮助的问题
,这是一个关于流控制的一般问题(答案显示了如何完成)。
macro_rules! loop_over_items {
($item:expr, $iter:ident, $code:block) => {{
let first = $item.first;
let mut $iter = first;
loop {
if let LoopIteration::Break = exec_iteration!($code) {
break
}
// Advance the iterator before proceeding, even if
// "continue" was used
$iter = $iter.next;
if first != $iter {
break;
}
}
}}
}