control_flow/lib.rs
1//! A hack to control control-flow outside closures.
2//!
3//! This crate allows one to do things like breaking loops outside a closure. It works through a
4//! a macro hack. Unless you really really need this, don't use it.
5//!
6//! # Example
7//!
8//! ```rust
9//! #[macro_use]
10//! extern crate control_flow;
11//!
12//! loop {
13//! let closure = || {
14//! defer!(break)
15//! };
16//!
17//! // Breaks the loop.
18//! run_loop!(closure());
19//! }
20//! ```
21
22/// A deferred control-flow command.
23#[must_use = "Without using the `Command` it doesn't do anything. You should execute it through `run!()` or `run_loop!()`."]
24pub enum Command<R, T> {
25 /// Pass the value on.
26 ///
27 /// This is not the same as return. What it does is that instead of breaking the control flow,
28 /// it passes on the value. That is, when `run!()` is called on this variant, the value that it
29 /// holds is evaluated to.
30 Give(T),
31 /// Return the value.
32 ///
33 /// This (when eventually executed) returns the given value.
34 Return(R),
35 /// Break a loop.
36 ///
37 /// This (when eventually executed) breaks the loop.
38 Break,
39 /// Continue a loop.
40 ///
41 /// This (when eventually executed) continues the loop to next iteration.
42 Continue,
43}
44
45/// Create a deferred control-flow command.
46///
47/// This takes a command (e.g. `return value`, `break`, `continue`, etc.) and creates the command
48/// in the form of the `Command` enum. This is deferred (that is, it is not runned instantly) until
49/// one executes the `Command`, which is done through `run!()` and `run_loop!()` depending on
50/// whether or not you are in a loop.
51#[macro_export]
52macro_rules! defer {
53 (return $val:expr) => { $crate::Command::Return($val) };
54 (return) => { defer!(return ()) };
55 (break) => { $crate::Command::Break };
56 (continue) => { $crate::Command::Continue };
57 ($val:expr) => { $crate::Command::Give($val) };
58 () => { defer!(()) }
59}
60
61/// Run a deferred control-flow command (outside a loop).
62///
63/// This takes a `Command` and runs it. This only works when not using loop-specific commands.
64#[macro_export]
65macro_rules! run {
66 ($command:expr) => {
67 match $command {
68 $crate::Command::Give(x) => x,
69 $crate::Command::Return(x) => return x,
70 _ => panic!("\
71 Using loop-dependent `Command` variants without loop mode enabled. Consider using \
72 `control_loop` instead.
73 "),
74 }
75 }
76}
77
78/// Run a deferred control-flow command within a loop.
79///
80/// This takes a `Command` and runs it.
81#[macro_export]
82macro_rules! run_loop {
83 ($command:expr) => {
84 match $command {
85 $crate::Command::Give(x) => x,
86 $crate::Command::Return(x) => return x,
87 $crate::Command::Break => break,
88 $crate::Command::Continue => continue,
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 #[test]
96 fn loop_break() {
97 let mut x = true;
98 loop {
99 run_loop!(defer!(break));
100 x = false;
101 }
102 assert!(x);
103 }
104
105 #[test]
106 fn loop_continue() {
107 let mut x = true;
108 for _ in 0..100 {
109 assert!(x);
110 run_loop!(defer!(continue));
111 x = false;
112 }
113 }
114
115 #[test]
116 #[allow(unused_assignments)]
117 fn return_early() {
118 let x = false;
119 run!(defer!(return));
120 assert!(x);
121 }
122
123 #[test]
124 #[allow(unused_assignments)]
125 fn store_ctrl() {
126 assert!((|| {
127 let mut x = defer!(return false);
128 x = defer!(return true);
129
130 run!(x);
131 unreachable!();
132 })());
133 }
134
135
136 #[test]
137 fn direct_value() {
138 assert!(run!(defer!(true)));
139 assert_eq!(run!(defer!()), ());
140 }
141}