Hacker News new | past | comments | ask | show | jobs | submit login

I know GAT is a "bigger feature" for the language but personally I'm most excited about `let else`. It is very common to write code such as:

  let foo = match do_something() {
    Ok(f) => f,
    Err(e) => {
      // Do some error handling, maybe log a warning, maybe skip this item and continue...
    }
  };
This small change makes that so much more clear, removes the silly first row and removes a level of indentation.

  let Ok(foo) = do_something() else {
    // Do some error handling, maybe log a warning, maybe skip this item and continue...
  };



Except the let-else version has one big disadvantage. The 'match' version has bound the error to `e`, so you can log that error or inspect it. The 'let-else' version can't read the error, so its log message can't be as useful. You can't check if it's a specific error, or include the error in logged messages.

It's still convenient, but in really limited circumstances, AFAICT. This week I'll be going through the various Rust projects I maintain at work, seeing if there's useful places for let-else.


> This week I'll be going through the various Rust projects I maintain at work, seeing if there's useful places for let-else.

If you want help from automation, you can wait a few days until the manual_let_else clippy lint arrives on nightly. It's going to be one of the pedantic lints, and recognizes some obvious places where let else would make sense (not all but many). It should arrive in one week-ish, depending on when the next clippy update is in nightly.


Update: I did go through every `match` statement in my 16k-line Rust project at work, and I found a number of places where it was useful. My commit to use let-else had 10 files changed, 27 insertions(+), 48 deletions(-).


you are right, not so useful for Result type, but still, it comes handy for Option types (since None doesn't hold anything).


Agree, I don't know if it's that useful for `Result<T>`, but for `Option<T>`, there has been a couple of times I've written

  if foo.is_none() {
    return;
  }
  let foo = foo.unwrap()
Now I can do simply

  let Some(foo_unwrapped) = foo else {
    return;
  }
which is prettier than the `if let (...)` to just unwrap it IMO.


Without let-else, you could write that as:

    let foo_unwrapped = match foo {
      Some(foo_unwrapped) => foo_unwrapped,
      None => return,
    };
Not as pretty, but you don't have to unwrap.


For Result it's probably more common to use ?, alternatively you could use

    let x_result = something;
    let Ok(x) = x_result else {
        ...
    }
But I expect that it will be used mostly with Option, as in "else continue" or "else break".


Couldn't you match on Err? That's how I do it with `if-let` anyway

    if let Error(e) = my_func() {
        //... Do something with e
    }


Yes, though that doesn't put the value of the `Ok` variant into any scope. At the end of the day, if you need maximum flexibility for whatever you're doing then you still need to reach for `match`, even if it's a bit more verbose.


I think in any instance where you need to unpack every variant, `match` will almost always be the least verbose option.


I mean, in all cases `if let` is for when there's nothing in other constructors that you want to talk about. That'll be every time you match on `Option<T>`, `Result<T, ()>`, or `Result<(), T>`, all of which come up at least occasionally, and will occasionally be other cases.

Edited to add: I don't mean to imply that you should always use `if let` for those cases - that may be but I reserve judgement on it.


So much of my code is this. I love this addition too.

I'm very impressed with how much good stuff is in this release. The Rust team is killing it.


Swift has `guard else`, including `guard let else` for preconditions, where you only put the error responding code and where the compiler forces you to exit the current context (e.g. return a value, throw an error, etc).

It is nice for collapsing pyramids of doom that come from normal precondition checking. You can put multiple comparisons or variable assignments into a a single guard as well, separated by commas.

It does not support binding to the error in a Result though, so for these you usually still get more readable code handling these with try/catch, with the corresponding nested code block.


How does this differ from the if-let syntax we already have? I'm a bit of a rust novice.


It's mostly about avoiding nesting. Honestly, I'm still pretty ambivalent about it. But, before you might do:

    if let Some(x) = maybe_x {
        // do stuff with x
    } else {
        return;
    }
now, you'd do:

    let Some(x) = maybe_x else {
        return;
    }
    
    // do stuff with x
If you have several instances of this kind of thing, the nesting can be pretty ugly and hard to read, so we'd sometimes do janky things to avoid the nesting, like:

    let x = if let Some(x) = maybe_x { x } else { return; };
or,

    if let (Some(x), Some(y)) = (maybe_x, maybe_y) {
        // do stuff with x and y with only one layer of nesting
    } else {
        return;
    }


It doesn't differ by much, here's an example of let-else replacing if-let: https://github.com/serprex/NULL/commit/9ab765c5089ce1f0e32ce...

In another codebase I had something like

   let msg = if let Ok(msg) = ... { msg } else { continue };
becoming

   let Ok(msg) = ... else { continue };


With if-let you still need to "pass down" the variable. It would look like:

  let foo = if let Ok(f) = do_something() {
    f
  } else {
    // Do some error handling, maybe log a warning, maybe skip this item and continue...
  };
It's similar to `let else` but you still need this extra `f` variable that isn't really adding clarity.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: