Proposal: `else` with `for` and `while`

in some languages like python, the else keyword could not only be used in if, but also in for and while loop.

I want to know is there any difficult prevent Rust from adding else to for and while loop?

$ python
Python 3.8.6 (default, Sep 30 2020, 04:00:38) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> i=3
>>> while i>0:
...   i-=1
... else:
...   print(i)
... 
0
>>> 

currently, Rust do not have such syntax, although we could using loop to simulate the while-else statement, and using map-filter to simulate the for-else loop, add both for-else and while-else syntax may give rust a more beautiful look.

let mut cond=true;
loop {
    if !cond {break {
        //else_statement
    }}
    //loop_body, may contais another break
}
---vs---
let mut cond=true;
while cond{
    //loop_body, may contais break
}else{
    //else_statement
}
------------------
let mut should_break=false;
iter.filter_map(|&x|{
    //loop_body, when need break, set `should_break` to true
    if should_break{Some(result)}else{None}
}).next().unwrap_or_else(||{
    //else_statement
})
---vs---
for &x in iter{
    //loop_body, include break here
}else{
    //else_statement
}

if we enable the else block for for-loop and while-loop, we could make Rust more cool than before: using let clause with for and while, not just loop.

let index=for index,x in iter.enumerate(){
    //...
    if meet_cond{break Some(index)}
}else{None};

before this proposal, we could using the following code instead:

let mut index=None;
for id,x in iter.enumerate(){
    //...
    if meet_cond{index=Some(id);break}
}
let index=index;

but we could only mark index as mut, and change it later, which is not cool.


update:
I want to add an else statement is because the actual problem I met:

what I wrote and some accept answers are quite ugly without while-let statement:

//what I wrote
impl Solution {
    pub fn find_content_children(mut g: Vec<i32>, mut s: Vec<i32>) -> i32 {
        let mut ans=0;
        g.sort_unstable();
        s.sort_unstable();
        let mut g=g.into_iter();
        let mut s=s.into_iter();
        while let Some(g)=g.next(){
            while let Some(s)=s.next(){
                if g<=s{ans+=1;break}
            }
            // if s.next() is none, there is no necessary to check the rest elements in the outer loop
        }
        ans
    }
}
// other accepted answer
impl Solution {
    pub fn find_content_children(mut g: Vec<i32>, mut s: Vec<i32>) -> i32 {
        g.sort();
        s.sort();
        let mut i = 0;
        let mut j = 0;
        while i < g.len() && j < s.len() {// if g is short and s is long, and most of elements in s smaller than it is in g, we have to perform much useless check for the statement `i < g.len()` 
            if g[i] <= s[j] {
                i += 1;
            }
            j += 1;
        }
        i as i32
    }
}

what we actually need is an else statement here:

impl Solution {
    pub fn find_content_children(mut g: Vec<i32>, mut s: Vec<i32>) -> i32 {
        let mut ans=0;
        g.sort_unstable();
        s.sort_unstable();
        let mut g=g.into_iter();
        let mut s=s.into_iter();
        while let Some(g)=g.next(){
            while let Some(s)=s.next(){
                if g<=s{ans+=1;break}
            }else{// if `s.next()` is `None`
                break//break the outer loop
            }
        }
        ans
    }
}

You probably want IRLO for this kind of thread, since it's about changing the language.

This has come up before, but I don't think there's appetite for it. Here's a post I found:

3 Likes

There's no technical reason preventing this from being added to Rust. However, you seem to be the victim of a common misunderstanding – language design does not mean that we add every possible feature that is technically possible.

Indeed, some have brought this up in the past (maybe even several times), but it doesn't seem to be worth the additional complexity. Rust already has 4 kinds of loops (loop, while, while let, for) and 3 kinds of branches (if, if let, match). These should be enough to express any reasonable, structured control flow.

Most (if not all) of your motivating examples can be rewritten in a way that they look like idiomatic Rust and avoid the confusion stemming from for...else. For example, the last snippet is simply Iterator::position().

If everything else fails, there's always the possibility of refactoring complicated control flow into its own function and even using ? for propagating Option, etc. That results in code that is much easier to understand and maintain than trying to jam excessively terse and clever control flow in the fewest possible lines.

7 Likes

I have a desperate urge to quote Dennis Ritchie again:

A language that doesn't have everything is actually easier to program in than some that do

I am really glad Rust does not have such a syntax. Until now I did not know any language had such an absurd thing.

But, regardless of what I think about that particular syntactic construct I despair at the though of Rust absorbing features from every possible language as users of those language discover Rust and infiltrate the developer base and shoehorn everything into a huge mess.

The fundamentals of all programming is "sequence, selection and iteration". One way to do each of those is quite enough and keeps things clear for everybody.

1 Like

I've updated the description in the main thread.
Until now I do not meet the condition that for-else is better than position, but I had met the condition that while-let seems necessary.(see update: in the main thread)

You are absolutely correct, but what you said is not all the truth.
Actually, using while is enough to express any reasonable, structured control flow.

if stmt{...}else{...}
->
let cond = stmt;// prevent calculate `stmt` twice
while stmt{...;break}
while !stmt{...;break}

(other conversions are similar.)
I want to introcuce while-let into the rust language is that, we seems using more complex syntax (and introduce more calculation) without while-let sequence.

sorry for the missing details(which is updated in the main thread)
the propose of while-else is similar to the while-let: to make program more beautiful (less redundant, more efficient,...)

the way to do iteration is something like iter.next(), I cannot believe that you think if-let is powerful but while-else worth your glad.
if you say "One way to do each of those is quite enough", you should first criticize if-let

As @scottmcm pointed out, the place for discussion of language changes is https://internals.rust-lang.org. I suggest that you ask this question there and then post a link here for people that want to continue the discussion.

Thank you for your suggestions.
I do not recognize IRLO as https://internals.rust-lang.org
I'll post the same thread there later.