Let binding with "|"

Why I can not write a code like this:

fn main() {
    let a = None;
    let b = Some(1);
    
    if let Some(c) = a | b {
        dbg!(c);
    }
}

I want to match the first one of a and b that can match successfully.

I think this is a natural idea for newbie.

Yeah, would be cool. But no operators are implemented for Option, so you'll need to do a.or(b). The standard library is cautious about implementing mathematical, logical, or bitwise operators for non-number types, although this one seems like it would be pretty uncontroversial and intuitive.

5 Likes

Thank you, a.or(b) is useful, but what I really want to say is:

enum Color {
  Rgb(u8, u8, u8),
  Hex(u32),
};

fn main() {
    let c1 = Color::Rgb(0, 0, 0);
    let c2 = Color::Rgb(10, 10, 10);
    let c3 = Color::Rgb(225, 0, 0);
    
    if let Color::Rgb(225, 0, 0) = c1 | c2 | c3 {
        dbg!("Red color!");
    }
}

Do you have any way to achieve the same goal?

It's not perfect but you can do [c1, c2, c3].into_iter().any(|c| matches!(c, Color::Rgb(225, 0, 0))).

5 Likes

Thank you. I know this.
But could you please explain why it's not perfect. I think that way is more intuitive and humane.

It's just a bit wordy.

This syntax doesn't work because | already has a different meaning:

if let x = 4 | 3 {}

would assign 4 to x according to your idea, but this is already valid code that assigns 7 to x.

2 Likes

To elaborate some more, the BitOr operator would need to be implemented for your enum, and it consumes both the operands to produce something new. Unless you have a logical way of choosing a "winner" based on values (in contrast with combining the operands[1] or choosing based on order[2]), one of these wouldn't work how you like even if you implement the operator:

    if let Color::Rgb(225, 0, 0) = c1 | c2 | c3 { ... }
    if let Color::Rgb(225, 0, 0) = c3 | c2 | c1 { ... }

Another approach which does work is

    if let (Color::Rgb(225, 0, 0), _, _)
    | (_, Color::Rgb(225, 0, 0), _)
    | (_, _, Color::Rgb(225, 0, 0)) = (c1, c2, c3)
    {

Or[3]

#[derive(PartialEq)]
enum Color { ... }

    const RED: Color = Color::Rgb(225, 0, 0);
    if let (RED, _, _) | (_, RED, _) | (_, _, RED) = (c1, c2, c3) {

In these cases, if any of the |-separated patterns match, the if let as a whole will match.

More about patterns.


  1. like what happens with integers ↩︎

  2. such as choosing the left operand ↩︎

  3. this requires the derived PartialEq, which is perhaps not what you want if an Rgb variant can be considered equal to a Hex variant ↩︎

3 Likes

Thank you.
I see.

Yes, it makes sense.