6 releases (2 stable)
Uses old Rust 2015
1.1.0 | Nov 14, 2015 |
---|---|
1.0.0 | May 25, 2015 |
0.2.1 | Mar 2, 2015 |
0.2.0 | Jan 20, 2015 |
0.1.1 | Nov 14, 2014 |
#2044 in Rust patterns
86 downloads per month
Used in 2 crates
12KB
Contains (ELF exe/lib, 7KB) a.out
A C-style for loop for Rust
A Rust macro implementing a C-style for
loop. See
the docs for more information.
lib.rs
:
A C-style for
loop in macro form.
This takes the form cfor!(initialiser; condition; step { body })
.
initialiser
is a statement evaluated before any iterations of the loop. Any variables declared here are scoped to thecfor!
invocation, that is, only usable insidecondition
,step
andbody
.condition
is an boolean expression evaluated at the start of each iteration. If it evaluates tofalse
iteration will stop.step
is an arbitrary expression which is executed at the end of each iteration (including ifcontinue
is called), beforecondition
is checked.
The initialiser and condition can be empty like C, but the step
cannot unlike C. A for
loop with no step is identical to a
while
loop.
When should I use it?
Only when cfor!
is clearer than the more declarative built-in
iterators, their
adaptors
and the for
loop. For example, the built-in iterator
functionality is more self-contained so there is less risk of
accidentally writing i
in a condition when j
was meant (I
personally get bitten by this semiregularly when writing nested
"2D" loops in C).
Furthermore, the adaptor methods linked above allow one to write concise, performant, reusable "functional" code in a way that is not possible to achieve using C-style iteration.
How to use it?
Add the repository as a normal cargo dependency, and include into
your crate with #[phase(plugin)]
. (See examples below.)
[dependencies.cfor]
cfor = "1.0"
Examples
Simple
A non-additive condition is not handled extremely naturally by
std::iter
, but is straight-forward to handle directly.
#[macro_use] extern crate cfor;
fn main() {
cfor!{let mut x = 1; x < 0x1000; x *= 2; {
println!("power of 2: {}", x);
}}
}
Intrabody condition
If a condition requires some extra computation to be checked (or
if there is some code that should always be evaluated, even if the
condition will be false
for a given iteration), the condition in
the cfor
header can be omitted.
#[macro_use] extern crate cfor;
fn main() {
cfor!{let mut x = 1; ; x *= 2; {
// ... setup ...
println!("handling power of 2: {}", x);
if x < 0x1000 { break }
// ... further handling ...
println!("handling power of 2: {}", x);
}}
}
Out-of-loop initialisation
Sometimes one may wish to have access to a variable outside the loop after it finishes so it has to be declared outside the loop, or one may be iterating over some presupplied/-computed value so there is no meaningful additional initialisation possible. The initialisation expression can be safely omitted in this case.
#[macro_use] extern crate cfor;
extern crate rand;
fn main() {
let mut x = 1u16;
cfor!{; x < 0x1000; x *= 2; {
println!("power of 2: {}", x);
// sometimes quit early
if x > rand::random() { break }
}}
println!("actually stopped at {}", x);
}
Handling continue
(Or, "why is the macro so complicated?")
Special effort is made to ensure that continue
acts correctly, a
naive macro defined as follows will cause continue
to also skip
evaluating step
, likely leading to undesirable behaviour like
infinite loops.
// WARNING: this is broken.
macro_rules! bad_cfor {
($init: stmt; $cond: expr; $step: expr; $body: block) => {
{
$init;
while $cond {
$body;
$step;
}
}
}
}
fn main() {
let mut true_counter = 0;
bad_cfor!{let mut i = 0; i < 10; i += 1; {
// manually avoid the infinite loop
if true_counter >= 50 { break }
true_counter += 1;
println!("i = {}", i);
// try to skip just i == 4
if i == 4 {
// but this skips the i += 1 leaving us
// on i == 4 forever.
continue
}
// ...more code...
}}
}
This is invoked in the same manner as cfor!
, but, if $body
contains a continue
, the $step
at the end of the loop body
will never be evaluated.
Handling multiple initializations and steps
Like C loops, cfor!
supports specfying multiple initializations and steps seperated by a comma.
#[macro_use] extern crate cfor;
fn main() {
cfor!{let mut x = 0, let mut y = x; x <= 10 && y <= 100; x += 1, y += 10; {
println!("x: {}, y: {}", x, y);
}}
}