Elliot Wasem
Elliot Wasem

Reputation: 45

Is there a way to do a for loop that is neither iterative nor linear?

Can I write a Rust for loop equivalent to this C code:

for(int i = 2; i <= 128; i=i*i){
    //do something
}

I'm only seeing things like

for i in 0..128 { /* do something */ }

or

let v = vec![0, 1, 2, /* ... */ ];
for i in v.iter() { /* do something */ }

Should I just use a while loop?

Upvotes: 3

Views: 357

Answers (2)

Shepmaster
Shepmaster

Reputation: 431539

You can always create a custom iterator that does whatever unique sequence you need:

struct Doubling {
    current: u64,
    max: u64,
}

impl Iterator for Doubling {
    type Item = u64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current > self.max {
            None
        } else {
            let v = Some(self.current);
            self.current *= 2;
            v
        }
    }
}

fn main() {
    let iter = Doubling { current: 2, max: 128 };
    let values: Vec<_> = iter.collect();
    println!("{:?}", values);
}

It's important to recognize that this logic (like the original C!) has nasty edge cases when the value is doubled beyond the size of the type.


In this particular case, you can also recognize that you have an exponential series:

fn main() {
    let iter = (1..8).map(|p| 2i32.pow(p));
    let values: Vec<_> = iter.collect();
    println!("{:?}", values);
}

If you want to get really experimental, check out Lazy sequence generation in Rust. Adapted here:

#![feature(generators, generator_trait, conservative_impl_trait)]

use std::ops::{Generator, GeneratorState};

fn doubling(mut start: u64, max: u64) -> impl Iterator<Item = u64> {
    GeneratorIteratorAdapter(move || {
        while start <= max {
            yield start;
            start *= 2;
        }
    })
}

fn main() {
    let iter = doubling(2, 128);
    let sum: Vec<_> = iter.collect();
    println!("{:?}", sum);
}

/* copy-pasta */
struct GeneratorIteratorAdapter<G>(G);

impl<G> Iterator for GeneratorIteratorAdapter<G>
where
    G: Generator<Return = ()>,
{
    type Item = G::Yield;

    fn next(&mut self) -> Option<Self::Item> {
        match self.0.resume() {
            GeneratorState::Yielded(x) => Some(x),
            GeneratorState::Complete(_) => None,
        }
    }
}

Upvotes: 7

DK.
DK.

Reputation: 59095

can I write a for loop equivalent to this C code:

That specifically, yes:

extern crate itertools;
for i in itertools::iterate(2, |&i| i*i).take_while(|&i| i <= 128) {
    // do something
}

But in general, no. There is no single, direct equivalent to all possible uses of C's for loop. If there's no way to write it using iterators then yes, you need to use a more general loop form:

{
    let mut i = 2;
    while i <= 128 {
        // do something
        i = i*i;
    }
}

Upvotes: 5

Related Questions