newspaint

Documenting Problems That Were Difficult To Find The Answer To

C-Style For Loop in Rust Language

A matter of some contention amongst Rust developers and users is the C-style for loop. At present it is not supported in the language – favouring iterators instead.

Simple counting jobs, such as counting from 0 to 99, are efficiently handled by the Rust compiler using the range() function.

In fact the compiler is so clever that loops over small iterated ranges can be efficiently unrolled:

fn main() {
    for i in range(0, 4u) {
        println!("{}", i)
    }
}

outputs:

0
1
2
3

… and the underlying assembler calls the print function four times rather than actually looping.

But what if you want to iterate something a little unorthodox, e.g.:

// test most specific 32-bit IPv4 netmask up to widest netmask
// in routing table
for ( mask = 4294967295; mask > 0; mask <<= 1 ) {
    ....
}

In Rust this can be expressed using the following form:

fn main() {
    for mask in std::iter::iterate(
        |x| x << 1, 
        0xFFFFFFFFu32
    ).take_while(
        |x| *x > 0
    ) {
        println!("{:t}", mask);
    }
}

Still – doesn’t roll off the tongue, does it.

It is possible to emulate a C-style for loop (contributed here) using a Rust macro:

#![feature(macro_rules)]

macro_rules! cfor {
    ($init: stmt; $cond: expr; $step: expr $body: block) => {
        {
            let mut first = true;
            $init;
            while {
                if first {
                    first = false
                } else {
                    $step
                }

                $cond
            } $body
        }
    }
}

fn main() {
    cfor!(let mut mask = 4294967295u32; mask > 0; mask <<= 1 {
        if mask & 0xFF000000u32 == 0xF0000000u32 {
            println!("{:t} (only top 4 bits set, continue)", mask);
            continue
        }
        
        if mask & 0xFF000000u32 == 0xC0000000u32 {
            println!("{:t} (only top 2 bits set, aborting)", mask);
            break;
        }

        println!("{:t}", mask);
    })
}

Which outputs:

11111111111111111111111111111111
11111111111111111111111111111110
11111111111111111111111111111100
11111111111111111111111111111000
11111111111111111111111111110000
11111111111111111111111111100000
11111111111111111111111111000000
11111111111111111111111110000000
11111111111111111111111100000000
11111111111111111111111000000000
11111111111111111111110000000000
11111111111111111111100000000000
11111111111111111111000000000000
11111111111111111110000000000000
11111111111111111100000000000000
11111111111111111000000000000000
11111111111111110000000000000000
11111111111111100000000000000000
11111111111111000000000000000000
11111111111110000000000000000000
11111111111100000000000000000000
11111111111000000000000000000000
11111111110000000000000000000000
11111111100000000000000000000000
11111111000000000000000000000000
11111110000000000000000000000000
11111100000000000000000000000000
11111000000000000000000000000000
11110000000000000000000000000000 (only top 4 bits set, continue)
11100000000000000000000000000000
11000000000000000000000000000000 (only top 2 bits set, aborting)

.. as expected.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: