For loop modifier blocks

I guess it is uncommon, however, I feel that languages should still be expressive and if a language can reduce the amount that flag variables are relied upon then the resulting programs will be easier to understand

Avoiding pervasive use of flag variables is certainly a good thing, but it doesn't necessarily need a language change. For example, you can use Itertools::with_position to conveniently do different things in different positions while iterating, or Itertools::intersperse to add things between items, or ...

5 Likes

Could it be a contextual keyword if it required the else.

I mean the following would be allowed:

for i in 0..1 {
    ...
}

for i in 0..1 {
    ...
} else {
    ...
}

for i in 0..1 {
    ...
} then {
    ...
} else {
    ...
}

but not:

for i in 0..1 {
    ...
} then {
    ...
}

This would be able to be done until the next round of keyword registration comes around.

This sort of makes sense since if you want to do something special when a loop normally exits but doesn’t break it is probably also when you want to do something if it break.


Or we could just forgo the whole thing and use for then and for else

I tried implementing this as a macro. (link) This can then be used like this:

fn main() {
    let mut list = String::new();
    custom_loop!(
        for a in 0..11; { // `;` needed because of macro limitation
            list.push_str(&format!("{}", a));
        } between {
            list.push_str(", ");
        } then {
            list.push_str(" END")
        }
    );
    println!("{}", list); // "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 END"
}

And combining all features

fn main() {
    let mut list = String::new();
    custom_loop!(
        for a in 0..11; {
            if a > 5 {
                break
            }
            list.push_str(&format!("{}", a));
        } between {
            list.push_str(", ");
        } then {
            list.push_str(" END")
        } else {
            list.push_str("EARLYEND")
        }
    );
    println!("{}", list); // "0, 1, 2, 3, 4, 5, EARLYEND"
}

and that expands to this:

fn main() {
    let mut list = String::new();
    {
        let mut _normal = true;
        let mut iter = IntoIterator::into_iter(0..11);
        let a = iter.next();
        if a.is_some() {
            _normal = false;
            let mut a = a.unwrap();
            loop {
                if a > 5 {
                    break
                }
                list.push_str(&format!("{}", a));
                a = match iter.next() {
                    Some(x) => {
                        list.push_str(", ");
                        x
                    }
                    None => {
                        _normal = true;
                        break
                    }
                };
            }
        }
        if _normal {
            list.push_str("END")
        } else {
            list.push_str("EARLYEND")
        }
    }
    println!("{}", list);
}

Should be useful if someone wants to try out this feature.

2 Likes

Is there a particular reason that the “then” and “else” take an expression whereas the “between” block takes a statement? If so, how is that enforced? Or is it?

No, the missing semicolons were typos. The macro itself operates on blocks and doesn’t care about the content as long as they return (). This matches the behavior of regular for loops.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.