6 ms·
`let else` looks strange to my eyes on first glance, but I can remember many cases when it would have been nice to have. (Not to mention, all Rust syntax used t
by mastax 4y ago
`let else` looks strange to my eyes on first glance, but I can remember many cases when it would have been nice to have. (Not to mention, all Rust syntax used to look strange to my eyes so that's not a very useful criterion).
Break from labeled blocks is a pretty niche thing. I can remember a few cases it would have been useful, but I'm not sure I'll remember the feature exists next time it comes up.
I'm glad that GATs are finally here. I tend to be wary of complex type-system things. I can remember being a new rust programmer around 1.0, dealing with some library with a perhaps-overly-clever API with complex traits and lifetimes and being very frustrated randomly changing code trying to get it to compile. It didn't help that rustc error messages and rustdoc weren't as good and we didn't have any sort of language server back then. Perhaps I'm just not very smart. But even with my focus on simply-typed APIs I've run into several cases where I couldn't do something the obvious way because it needed GATs.
I used to be a lot more involved with Rust, reading a lot of /r/rust and blog posts and issue trackers. I get a warm wistful feeling seeing things like GATs, rust-analyzer, and MIR inlining get stabilized after all these years.
- zozbot234 4y ago> `let else` looks strange to my eyes on first glance AIUI, this is a rather specialized feature that only supports diverging code in the "else" block. I assume that the weird syntax was purposely chosen to suggest something like that.
- queuebert 4y agoHow does this improve on binding the `Err`? Edit: I see now in the first example: let (Some(count_str), Some(item)) = (it.next(), it.next()) else { panic!("Can't segment count item pair: '{s}'"); }; Binding the error here would be much verbose, since both must succeed. Edit2: Doh, obviously `None` in this example, not `Err`.
- kibwen 4y agoI don't find the syntax weird, I think it's perfectly cromulent with the existing capabilities. To wit, pattern matching in `let` has always been possible: let (x, y) = (1, 2); ...But only for patterns that can never fail. For enums, not all patterns are irrefutable: let Some(x) = Some(42); // error, Rust can't tell that RHS isn't `None` Note that some languages with pattern matching do allow the above, e.g. OCaml does, and just panics if the match doesn't succeed (so equivalent to `let x = Some(42).unwrap()` in Rust). But Rust favors exhaustive pattern matching, so that wasn't a good fit. The way that Rust solves this is via `if`-style constructs. So you already have `if let`: if let Some(x) = Some(42) { // `x` exists in here } This produces an inner scope where the contents of the enum are bound. And you can use `else` here as well: if let Some(x) = Some(42) { // `x` exists in here } else { // branch where `x` doesn't exist } So the `let else` syntax feels like a natural continuation to me: let Some(x) = Some(42) else { // branch where `x` doesn't exist } // `x` exists here This reduces the nesting required in several common cases of using enums, which makes code easier to read. (Of course, the interesting thing is that since `x` exists in the outer scope, the compiler requires the inner scope to never reach the outer scope (it must "diverge", by returning or panicking or infinite looping or whatever), which is an extra restriction above what a normal `if else` requires.)
- tialaramex 4y agoSo it just checks the type of the "diverging" else block is Never or something ?
- kibwen 4y agoYes, the `else` branch in `let else` is required to diverge.
- pavon 4y ago> Break from labeled blocks is a pretty niche thing. I can remember a few cases it would have been useful, but I'm not sure I'll remember the feature exists next time it comes up. Yeah, I feel like the vast majority of the time this would be better handled by splitting out that block into a function. There are situations where there the number of parameters needed to pass in would get unwieldy, but that is where I start thinking about if all that loose state is intrinsic to the problem, or a sign that I need to refactor my datastructures.
- zozbot234 4y ago> Yeah, I feel like the vast majority of the time this would be better handled by splitting out that block into a function. It's a nice idea, and Rust supports "local" fn definitions inside blocks so it would be quite elegant as well.
- edflsafoiewq 4y agoThe borrow checker is "smarter" within a single function body though, so factoring code into a separate function is not necessarily as easy as in other languages.
- Ar-Curunir 4y agoYou can use it with a closure to get the best of both worlds
- saghm 4y agoI ran into this issue years ago (possibly even before labeled exit from loops or even NLL, although I'd have to dig through some old code to confirm this). I had some logic that I wanted to short-circuit, but moving everything to a separate method call would have required an extra borrow that would not have been allowed. I don't think that I'll end up needing to use this feature much, but I'm glad to finally have it as an option for the rare cases when it will improve the readability of my code.
- mjw1007 4y ago
- kibwen 4y agoNote that this feature has precedence from Swift, where it's called `guard let`: https://www.hackingwithswift.com/quick-start/understanding-swift/when-to-use-guard-let-rather-than-if-let https://www.hackingwithswift.com/quick-start/understanding-s... . Rust's syntax just drops the `guard` part.