Does Rust 2018 support "if let" chaining?
Asked Answered
P

3

21

I'm parsing a vector of tokens, each of enum type. This means I get a lot of code like:

if v.len() >= 3 {
    if let Token::Type1(value1) = &v[0] {
        if let Token::Type2(value2) = &v[1] {
            if let Token::Type(value3) = &v[2] {
                return Parsed123(value1, value2, value3);
            }
        }
    }
}

This is pretty ugly - and I've worked out that I can do this to make it a little nicer:

if v.len() >= 3 {
    if let (Token::Type1(value1), Token::Type2(value2), Token::Type3(value3)) =
        (&v[0], &v[1], &v[2])
    {
        return Parsed123(value1, value2, value3);
    }
}

But honestly, its not much better.

However, there's some closed issues / RFCs for chaining these conditions and "if let" bits in what feels a lot more ergonomic way -- Tracking issue for eRFC 2497 "if- and while-let-chains take 2" and Support && in if let expressions -- this would let me write something like:

if v.len() >= 3 &&
    let Token::Type1(value1) = &v[0] && 
    let Token::Type2(value2) = &v[1] && 
    let Token::Type3(value3) = &v[2]
{
    return Parsed123(value1, value2, value3);
}

However, I can't seem to get this to compile in my copy of nightly Rust with edition="2018" (exact version is 1.32.0-nightly (653da4fd0 2018-11-08)). So either I've got the syntax wrong or I've misinterpreted the RFCs / issues and this feature hasn't landed yet. Either way, I'd love some info on how this feature stands.

Palocz answered 10/11, 2018 at 2:17 Comment(6)
You might find this library interesting.Theressa
@Michail That library looks awesome! I was wondering whether this could be achieved through macros. I figured it was worth its own answer and have added one. Hope that's OK with you.Palocz
How about if let [Token::Type1(value1), Token::Type2(value2), Token::Type3(value3), ..] = v?Vilify
@L.F. That is certainly an option now. This only landed in stable rust in 1.42.0 on 12 Mar 2020. You can see the release notes here. blog.rust-lang.org/2020/03/12/Rust-1.42.html#subslice-patterns.Palocz
Oh that's right .. Time for me to learn some history.Vilify
Just to note, let_chains that would allow this was due to be stabilized in Rust 1.64, but was then reverted as it was not implemented correctly.Jacintha
U
20

RFC #2497 has not been implemented yet. The GitHub issue you linked is only for describing how to deal with the ambiguity.

To enable the second interpretation in the previous section a warning must be emitted in Rust 2015 informing the user that [...] will both become hard errors, in the first version of Rust where the 2018 edition is stable, without the let_chains features having been stabilized.

So no, you cannot use the syntax yet, but instead use tuples as a workaround, as you already did.

if v.len() >= 3 {
    if let (Token::Type1(value1), Token::Type2(value2), Token::Type3(value3)) =
        (&v[0], &v[1], &v[2])
    {
        return Parsed123(value1, value2, value3);
    }
}
Unnumbered answered 10/11, 2018 at 7:45 Comment(2)
Notably, when the RFC is implemented, both Rust 2015 and 2018 will support it.Inigo
Ah, so let expressions in this position are experimental see issue #53667 <github.com/rust-lang/rust/issues/53667> for more information rustc(E0658) means "Sorry, not implemented"..Foucquet
P
8

While hellow is correct that RFC #2497 is not yet supported in 2018 (and 2015), I felt the if_chain library mentioned by Michail was worthy of an answer.

The if_chain library provides a macro that transforms some code that is almost in the form of RFC #2497 into valid Rust.

You can write:

if_chain! {
    if v.len() >= 3;
    if let Token::Type1(value1) = &v[0]; 
    if let Token::Type2(value2) = &v[1]; 
    if let Token::Type3(value3) = &v[2];
    then {
        return Parsed123(value1, value2, value3);
    }
}

which the compiler treats as:

if v.len() >= 3 {
    if let Token::Type1(value1) = &v[0] {
        if let Token::Type2(value2) = &v[1] {
            if let Token::Type(value3) = &v[2] {
                return Parsed123(value1, value2, value3);
            }
        }
    }
}
Palocz answered 11/11, 2018 at 22:58 Comment(0)
P
5

As mentioned in the comments by L.F., in 2020 there is now another alternative. It still doesn't give us chained if let, but does allow us to match on slices - which is enough to make this example quite neat. The code could now be written as

if let [Token::Type1(value1), Token::Type2(value2), Token::Type3(value3), ..] = v {
   return Parsed123(value1, value2, value);
}
Palocz answered 8/8, 2020 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.