Skip to content

Support && in if let expressions #929

@mdinger

Description

@mdinger

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions