Is it possible to use pattern matching guards in a `while let`?
Asked Answered
V

3

26

I have a while let loop which goes over an iterator of Result and uses pattern matching; it goes over the iterator until it either hits an Err or the Ok's value is an empty string:

while let Some(Ok(a)) = some_iterator.next() {
    if a == "" {
        break;
    }
    // ...
}

This code works fine. However, I think the if statement looks ugly and is probably not idiomatic Rust. In match statements, guards can be used in pattern matching, like so:

match foo {
    Some(Ok(a)) if a != "" => bar(a)
    // ...
}

This would be ideal for my while let loop, although the pattern matching employed there doesn't seem to support it, causing a syntax error:

while let Some(Ok(a)) = some_iterator.next() if a != "" { // <-- Syntax error
    // ...
}

Is there any way of using guards like this in the condition of a while let? If not, is there a better way of breaking out of the loop if an empty string is found?

Voluptuary answered 26/10, 2016 at 18:4 Comment(2)
and is probably not idiomatic Rust — FWIW, I've written code like that; doesn't seem bad to me.Invention
I'd encourage answerers to focus on answering the primary question (guards in a while let). OP should probably ask another question for the "is there a better way" aspect.Invention
H
20

No, while let and if let patterns cannot have guards. There has been some discussion about changing that (e.g. here), but nothing has been decided yet.

Regarding alternatives, I think your version is pretty clear and I can't think of any ways to really improve on that.

Housebroken answered 26/10, 2016 at 19:40 Comment(0)
H
3

To those that find this from a web search, here's the if-let-chains RFC that addresses conditions in if let expressions:

https://rust-lang.github.io/rfcs/2497-if-let-chains.html

Haff answered 30/3, 2023 at 10:52 Comment(6)
And so this is now in the language, and the original answer, while correct at the time, no longer is? You can now do what they want?Moll
@KevinAnderson Not yet, it's still an unstable feature, which means still in development and not usable without the nightly compiler and a feature flag.Inflationism
I literally just went over to play.rust-lang.org and discovered this myself, but thanks for the confirmation. Compiler help says ` add #![feature(let_chains)] to the crate attributes to enable` on nightly. Issue Tracker for it.Moll
There is also an alternate proposal for an is operator which would solve the same problem although more intuitively and with boolean expressions rather than magic. github.com/rust-lang/rfcs/pull/3573Tifanytiff
The is operator is still "magic", it's just a different way of writing pattern matching. It uses a different keyword to the 2 already in Rust for this purpose: match and let already introduce bindings and perform pattern matching. I see no benefit that is adds to the language.Haff
Appendices B.8 and B.9 of the if-let RFC describe issues with is from a community survey that was done on syntax: github.com/rust-lang/rfcs/blob/master/text/…Haff
M
1

For all intents and purposes, your code is the most legible.

But you can get creative (including with tools which weren't available at the time of asking).

Cramming a condition into a pattern match :

while let (Some(a), true) = (iterator.next(), independent_condition()) {
    //do stuff
}

Putting the filter inside the iterator :

while let Some(a) = iterator.take_while(|a| a!= "").next() {
    //do stuff
}

Keeping in mind that in simple cases when you end up with a single iterator which does not depend on things you borrow inside the loop, you no longer need while and a Some pattern de-structuring :

for a in iterator.take_while(|a| a!= "") {
    //do stuff
}

skip_while and scan are similar advanced method on iterator which may come in handy in these kind of situations.

Miscegenation answered 5/2 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.