3

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Possible duplicate of [Are C style for loops that step by a different amount each iteration possible in Rust?](https://stackoverflow.com/questions/43823042/are-c-style-for-loops-that-step-by-a-different-amount-each-iteration-possible-in) – Boiethios Feb 28 '18 at 14:13

2 Answers2

7

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,
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    I mean... *yes*, but that's not exactly helping Rust's reputation for being long-winded. This is "using the death star to explode the planet to kill a spider" levels of overkill... – DK. Feb 28 '18 at 13:50
  • 3
    *sees latest edit* Ok, now you're doing this on purpose. :P – DK. Feb 28 '18 at 14:01
  • @DK. I mean, only a little bit ;-). However, as I mention in the linked question, I think the `GeneratorIteratorAdapter` will be a part of the standard library in *some* form, leaving the `doubling` function as "reasonably" small. – Shepmaster Feb 28 '18 at 14:12
5

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;
    }
}
DK.
  • 55,277
  • 5
  • 189
  • 162