1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! # loop_let
//!
//! This crate provides a macro for a new control flow mechanism in Rust proposal `loop let`.
//! Which is a immutable loop pattern that is meant for tail call recursive algorithms earning
//! tail call optimization for free.
//!
//! This crate provides the `loop_let!` macro that implements the `loop let` pattern. The syntax
//! is as follows:
//!
//! ```rs no_run
//! loop_let!((pattern) = (initial); {
//! // code ...
//! });
//!
//! loop_let!(fails (pattern) = (initial); {
//! // code ...
//! });
//! ```
//!
//! The `pattern` is the pattern that will be matched against the input. The `initial` is the
//! initial value of the input. The code block is the code that will be executed in every iteration
//! of loop. It's important to notice that the code block must return either a `Continue` or
//! a `Break`.
//!
//! `Continue` is used to set the input value for the next iteration of the loop. `Break` is
//! used to break the loop and return the value.
//!
//! The `fails` keyword is used before the pattern when using fallible patterns. This way, the loop
//! breaks once the pattern fails. By using the `fails` keyword, the `loop_let` macro will return
//! `()` (this means you can only use `Break(())`). So it can't be used as an expression.
//!
//! # Why?
//!
//! The main purpose of this crate is to encourage the utilization of tail call recursive algorithms
//! in Rust rather than the traditional recursion which relies on the compiler to optimize the code.
//!
//! Some other earnings of using the `loop let` pattern in comparison to other tail call recursive
//! approaches are:
//!
//! - Support for pattern matching (even fallible patterns)
//! - Loops as an expression
//! - Avoids the need for `mut`
//! - Avoids the need of tail call optimization at the compiler level
//! - Increased readability
//!
//! This pattern is used in Clojure with the loop/recur pattern where a loop might return a value
//! or call itself with new arguments.
//!
//! This is a proposal for a new control flow mechanism in Rust as a language construct `loop let`.
//!
//! ```rs no_run
//! // Proposed syntax (no need for `fails` keyword)
//! loop let (pattern) = (initial) {
//! // ...
//! continue (new_input); // New syntax for continue
//! // ...
//! break (result);
//! }
//! ```
/// Implementation of the `loop let` immutable loop pattern. This implements a loop that
/// is meant for tail call recursive algorithms earning tail call optimization for free.
///
/// The syntax is as follows:
///
/// ```rs no_run
/// loop_let!((pattern) = (initial); {
/// // code ...
/// });
/// ```
/// The `pattern` is the pattern that will be matched against the input. The `initial` is the
/// initial value of the input. The code block is the code that will be executed in every iteration
/// of loop. It's important to notice that the code block must return either a `Continue` or
/// a `Break`.
///
/// `Continue` is used to set the input value for the next iteration of the loop. `Break` is
/// used to break the loop and return the value.
///
/// When using fallible patterns, the `fails` keyword must be used before the pattern.
/// This way, the loop breaks once the pattern fails. By using the `fails` keyword, the
/// `loop_let` macro will return `()` (this means you can only use `Break(())`). So it can't
/// be used as an expression.
///
/// # Panics
///
/// If a fallible pattern is used without the `fails` keyword, the loop will panic once the
/// pattern matching fails.
///
/// # Example
/// ```rs
/// use loop_let::loop_let;
///
/// let fib = loop_let!((n, curr, next) = (6, 0, 1); {
/// if n == 0 {
/// Break(curr)
/// } else {
/// Continue((n - 1, next, curr + next))
/// }
/// });
///
/// assert_eq!(fib, 8);
/// ```