Support for `let` expressions within `match`

Simple request, feel free to dunk on me if this is an unreasonable or inappropriate request. In addition, if there is some syntax already like this, then excuse my ignorance and I would appreciate being pointed to it.

I have been working on a project that extensively uses the return type of Result<MyEnum, MyError>, but this can be extended to other nested enums like Option<MyEnum>

Similar to how if let allows the user to explore the conditional of a single branch of the "parent" enum (e.g. if let Ok(value) = result { .. }, it would be nice to reduce the syntax overhead when attempting the following:

// result: Result<MyEnum, MyError>
if let Ok(value: MyEnum) = result {
  match value {
    MyEnum::Variant1 => (),
    MyEnum::Variant2 => (),
  }
}

or

// result: Result<MyEnum, MyError>
match if let Ok(value: MyEnum) = result { result } else { continue }
    MyEnum::Variant1 => (),
    MyEnum::Variant2 => (),
}

and reduce it to something along the lines of:

// result: Result<MyEnum, MyError>
match let Ok(value: MyEnum) = result {
  MyEnum::Variant1 => (),
  MyEnum::Variant2 => (),
}

with the assumption that the Err case will skip the statement all-together, but the option to handle it

the window of context for let Ok(value) = result will, as a result, stay consistent between the if implementation as well as this proposed match one

I have used match match in the past for something similar. I am very ignorant on the development lift which is required for something like this, nor if people are privy to it, but it's something I am personally dealing with

In most cases I don't see this adding much over the bubble operator ?. In particular once try blocks are stable. But usually when dealing with results your block could be written

match result? {
    MyEnum::Variant1 => (),
    MyEnum::Variant2 => (),
}

I understand, but this is restrictive since the ? operator will return upon failure, which is not necessarily what I want. The if let operation is purely explored when the condition is met, but doesn't return upon failure, simply continues.

In addition, this is restrictive since the ? operator can be overloaded (AFAIK) using std::ops::Try, which only supports 2 types, one for short-circuiting and one for not. Consider the following example:

enum CodecType {
  Encoder,
  Decoder,
}
enum Data {
  Key(CodecType),
  Length(CodecType),
  Value(CodecType),
  Other,
}

// data: Data
match let Key(codec) = data {
  CodecType::Encoder => (),
  CodecType::Decoder => (),
}

Where the ? operator can not handle all of these cases

Also, I can use your example given a Option or Result type by making an additional function call (since it returns, rather than continutes), but I'm not sure how the compiler / LLVM will treat it, since I don't necessarily want to be making an additional subroutine call just for prettier syntax

An alternative that already works:

match data {
    Data::Key(CodecType::Encoder) => {},
    Data::Key(CodecType::Decoder) => {},
    _ => {}
}
1 Like

For your original example, another way to write it would be something like:

match result {
    Ok(MyEnum::Variant1) => (),
    Ok(MyEnum::Variant2) => (),
}

It sounds like what you're asking for is a way to avoid writing the Ok( and ) repeatedly on each branch?

In that case, I think the easiest way to write what you'd asking for, rather than any of the variants you mentioned, would be:

let Ok(value) = result else { continue };
match value {
    MyEnum::Variant1 => (),
    MyEnum::Variant2 => (),
}

I think that's clearer than attempting to combine the two constructs.

5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.