Refutable Pattern in For Loop

I'm just curious as to why A doesn't exist as a shorthand for B or C.

fn main() {
    let opts = [Some(1), None, Some(2)];

	// A
	for Some(n) in opts {
		println!("{n}");
	}

	// B
	for o in opts {
		if let Some(n) = o {
			println!("{n}");
		}
	}

	// C
	for o in opts {
		let Some(n) = o else { continue };
		println!("{n}");
	}
}

For your example you can use flatten.

1 Like

The “why” I see is that if for’s pattern skipped non-matching items, then it would be possible to accidentally write a for loop that skipped items, when the intent was just to do destructuring, not filtering. You can add filtering ability with an if let, but you cannot subtract filtering from a for that had it built in.

12 Likes

there's ambigiouty here, if such syntax were to be supported, what's the obvious intention???

for instance, besides the "filtering" behavior like case B or C in your example, there's also the possibility of take_while() behavior. compare:

for n in opts.into_iter().filter(Option::is_some) {
    //...
}
for n in opts.into_iter().take_while(Option::is_some) {
    //...
}

your example can just be written with filter_map():

// using closure
for n in opts.into_iter().filter_map(|x| x) {
}

// using a named function
fn id<T>(x: T) -> T { x }
for n in opts.into_iter().filter_map(id) {
}

// wrapping in a helper function:
fn filter_some<T>(it: impl IntoIterator<Item = Option<T>>) -> impl Iterator<Item = T> {
    it.into_iter().filter_map(|x| x)
}
for n in filter_some(opts) {
}

syntax sugar is like real sugar, if you desire too much of it, it's bad for your health.

10 Likes

I'd like to make a case for refutablility in for loops.

A standard "for ... in" loop translated to English would be "For each element in this iterable do", so a refutable pattern would be fundamentally different from a while loop ("While a condition is met, do").

To me, it seems obvious that a "for Pattern(...) in" stands for "For each element in this iterable matching the pattern do". I would find it to be a syntax sugar that is very pleasant to read, write and understand while reducing cognitive complexity of code.

Thing is that you have to pick continue or break here, and it's not obvious that one is better than the other.

So thus that's not done as part of the for sugar, but by the programmer writing which one they want -- either directly with let-else, or by using one of the adapters that encodes it, like map_while (break on non-match) or filter_map (continue on non-match).

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.