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.

8 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.

6 Likes