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}