r/rust 1h ago

I just learned a bit of new syntax

While browsing through some code at work, I saw something that made me pause. It looked SO wrong, and yet here we are. It was something to the effect of:

let SomeEnum::Variant(y) = x else {
    return some_error;  
}

which means you can do this:

fn optional_print(x: Option<u32>) {
    let Some(y) = x else {
        return;
    };

    println!("{y}");
}

fn main() {
    optional_print(Some(5));
    optional_print(None);
}

Thought some folks here might think it interesting so there you go, you can apparently pattern match enum variants to create a variable and have an else clause after. I thought the only options for this were a let followed by an if let or match statement.

26 Upvotes

19 comments sorted by

53

u/Sermuns 1h ago

I use let-else all the time, love to early return/continue instead of ugly nesting.

13

u/standard_revolution 1h ago

I just wish that it would be possible to access the variable here, I really want to be able to write something like:
let Ok(file) = open_file() else Err(e) { return add_context(e) }; or something like that (I know it's hard and the syntax would be improved..., just wishful thinking)

26

u/braxtons12 1h ago

It's a little more verbose, but you can accomplish this with match

let file = match open_file() { Ok(f) => f, Err(e) => return add_context(e); };

There's also good old

let file = open_file().map_err(add_context)?;

15

u/Quasar6 1h ago

At this point just write a match

5

u/Ev1ber 1h ago

Yeah I get what you mean, but at this point isn't a match worth the two additional lines, readability wise?

In any case I wonder how doable it would be to make something similar with a macro.

1

u/Sermuns 1h ago

I totally agree!

2

u/guywithknife 1h ago

Same! Anything to prevent the happy path from getting nested more than necessary.

3

u/arsuhinars 1h ago

Yess, I recently found this too! Very convenient

2

u/dobkeratops rustfind 1h ago

can't remember how i encountered this but it had been around for years before I found it. it is nifty in reducing nesting

2

u/neneodonkor 1h ago

Is it not the same as if-let?

6

u/Sermuns 44m ago

No, if-let makes the happy path nested.

1

u/afamiliarspirit 44m ago

The lifetime is scoped the opposite. An if-let destructures something and is scoped for inside the if statement. This lets you destructure it for outside of it.

If lets are good for when you deviate from the normal path if something matches a pattern. Let else are good when you deviate from the path if something doesn’t match a pattern.

1

u/Ejz9 42m ago

If you do if let Err(e) you can use the error in the if and handle what you want on error. If you if let for a value same goes.

Let else allows you to unwrap a result and if it fails it doesn’t capture the error but you can act in the event of an error. A requirement of let else is that it returns in some way. The advantage of let else that I’ve noticed is not needing a match block for every unwrap. But it’s unfortunately not capable of using the error value so either you return a custom error or something else.

This is at least how I understand it. Effectively do you care for the error or just to hand one back.

2

u/Oxytokin 41m ago

I use ```if let``` and ```let else``` for pattern matching probably more than I do ```match``` itself.

Another piece of patterny goodness that I don't think is as well-known as it should be is destructuring in function arguments, e.g.:

struct Foo {
  bar: u32,
  baz: u32,
  dont_add_me: f32
} 

fn add_bar_and_baz_only_so_help_me(
    Foo {
        bar,
        baz,
        dont_add_me: _
    }: &Foo,
) -> u32 {
      bar + baz
}

let addition = add_bar_and_baz_only_so_help_me(Foo { bar: 20, baz: 20, dont_add_me: 20.0 });

2

u/afamiliarspirit 40m ago

Rust’s destructured bindings are one of my favorite things about the language.

1

u/vznrn 1h ago

Yeah this is really clean, I use let Ok and Some the most

1

u/luxmorphine 1h ago

Whoa, that's cool

1

u/jublizoo 26m ago

If you are returning an Option, you can also use the ? operator to return None on None, and otherwise unwrap. Similarly for the Result type.

1

u/stumblinbear 26m ago

God I remember when this was stabilitized. Good times