Michael Anderson
Michael Anderson

Reputation: 73480

Does Rust 2018 support "if let" chaining?

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.

Upvotes: 21

Views: 7681

Answers (3)

Michael Anderson
Michael Anderson

Reputation: 73480

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);
            }
        }
    }
}

Upvotes: 8

Michael Anderson
Michael Anderson

Reputation: 73480

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);
}

Upvotes: 5

hellow
hellow

Reputation: 13430

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);
    }
}

Upvotes: 21

Related Questions