Skip to content

Support && in if let expressions #929

Closed

Description

Update:

@kballard has a better suggestion than I did at #929 (comment) . Similar type of idea but borrows from Swift.

Original idea below.


Seems like a good idea to support && in if let expressions. I'm not sure about ||. It seems good from a consistency standpoint but I'm not sure if the the fact that the destructuring could result in different types could present a problem or not. In #525 it was a problem so I'd anticipate a problem here as well.

fn main() {
    // `op_op` (option option)
    let op_op = Some(Some(6));

    // Leads to deep nesting which is bad.
    if let Some(op) = op_op {
        if let Some(i) = op {
            println!("Matched {:?}, {:?}!", op, i);
        }
    }

    // It'd be nice to support the `&&` operator. `op_op` destructures
    // then `op` destructures. These would be directly equivalent.
    if let Some(op) = op_op &&
           Some(i) = op {
        println!("Matched {:?}, {:?}!", op, i);
    }

    // With `else`
    if let Some(op) = op_op {
        if let Some(i) = op {
            println!("Matched {:?}, {:?}!", op, i);
        } else {
            println!("Didn't");
        }
    } else {
        println!("Didn't");
    }

    // Would be replaced with
    if let Some(op) = op_op && 
           Some(i) = op {
            println!("Matched {:?}, {:?}!", op, i);
    } else {
        println!("Didn't");
    }

    let (op1, op2) = (Some(7), Some(8));

    // If `&&` is allowed, `||` shoud be also. Take first branch that
    // destructures. Otherwise do nothing.
    if let Some(i) = op1 ||
           Some(i) = op2 {
        println!("Matched {:?}!", i);
    }

    // Is equivalent to:
    if let Some(i) = op1 {
        println!("Matched {:?}!", i);
    else if let Some(i) = op2 {
        println!("Matched {:?}!", i);
    }

    // This is invalid because either `i` or `j` might appear in the
    // expression. The destructuring must use the same identifiers.
    if let Some(i) = op1 ||
           Some(j) = op2 {
        println!("Matched {:?}!", i);
    }

    // Adding an `else` clause:
    if let Some(i) = op1 {
        println!("Matched {:?}!", i);
    else if let Some(i) = op2 {
        println!("Matched {:?}!", i);
    } else {
        println!("Didn't");
    }

    // Would be equivalent to:
    if let Some(i) = op1 ||
           Some(i) = op2 {
        println!("Matched {:?}!", i);
    } else {
        println!("Didn't");
    }

    // These can go really deep:
    let op_by_4 = Some(Some(Some(Some(6))));

    // Would be either:
    if let Some(op_by_3) = op_by_4 {
        if let Some(op_by_2) = op_by_3 {
            if let Some(op) = op_by_2 {
                if let Some(i) = op {
                    println!("Matched {:?}, {:?}, {:?}, {:?}, {:?}!",
                              op_by_4, op_by_3, op_by_2, op, i);
                }
            }
        }
    }

    if let Some(op_by_3) = op_by_4 &&
           Some(op_by_2) = op_by_3 &&
           Some(op) = op_by_2 &&
           Some(i) = op {
        println!("Matched {:?}, {:?}, {:?}, {:?}, {:?}!",
                 op_by_4, op_by_3, op_by_2, op, i);
    }
}

Technically, && is typically used with bool so & or some other identifier (and?) could be valid here as well. I thought && made sense though because this is being interpreted in a slightly similar fashion to a boolean expression.

EDIT: Added else to examples and an example of a deeper nesting example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions