Macros 如何在宏中包装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

在Rust中,可以编写do-while样式循环:

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;
            }
        }
    }}
}