I've noticed for the past few years whilst programming in rust that there is no clean conditional return for instance: when a function needs to exit early
in rust:
if condition {return}
other languages:
if(condition)return;
The rust syntax feels kinda clunky when it comes to things like that. This also happens for conditional breaks and continues but are less of a problem as they are rarely needed. Now I wanted to get some of your opinions about a syntax like this:
return if condition;
return value if condition;
continue if condition;
or even this syntax for the return iftrue:
rift condition;
If you have any ideas or concerns about this please let me know because I would love for rust to have something like this.
An extra pair of braces imho is ok for readability. You still have the extra parenthesis in if(cond)return;. Or if you're talking about rustfmt automatically splitting it to two lines, actually clang-format does the same for C code.
A simple alternatives would be to write a macro rift!(cond); that expands to if (cond) return;, or use Result so you can early exit with a single ?.
I don’t see much value in new syntax here though. Usual, relevant concerns solved by new syntax sugar are too lengthy syntax, and/or added levels of indentation. I don’t see any such concerns with existing if condition { return; }. The only possible problem I see is that the normal way rustfmt formats it adds a number of extra lines, turning
if condition() { return }
into
if condition() {
return;
}
If that’s the problem to be solved, perhaps (if it doesn’t exist already) a way to configure rustfmt differently is all that’s necessary?
I have a hard time imagining where simply returning is the correct way to handle conditions. Those cases exist, but you'd almost always want to return some specific value if you do an early return, like the ? operator does for error handling. If you're returning a value anyway, a couple of braces make no difference for readability.
This makes me think that you over-rely on side effects, including global mutable state, instead of properly propagating data between functions. That's not a pattern which I would want to encourage in Rust.
An example of more structured early returns is the anyhow::ensure! macro. Most of the time that's the preferred solution, and you can write a similar macro if you're doing a lot of early break or continue calls.
yeah I should have specified that I didn’t meant for this to discourage proper data propagation. but more simply for filtering logic in systems like the in the code my reply to Hoblovski
Correct me if I’m wrong but from my understanding a if loop in rust requires a {} body, in which case the parser could differentiate between the two statements
I can't say since I'm not familiar with parsing in rustc but it looks like rustc uses a recursive descent (pretty much like PEG) way. Just from my experience with PEG parsing for my toy languages, parsing is quite subtle and can break very unexpectedly, especially with two different constructs sharing the same prefix.
Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `FromResidual<GuardFailure>` for type `Option<_>`
--> src/lib.rs:18:1
|
18 | impl<T> FromResidual<GuardFailure> for Option<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> FromResidual for Option<T>;
That seems like a bug / undesirable error, right? cc @scottmcm
In case that’s an unavoidable or not-going-to-be-fixed-anytime-soon consequence of overlap detection on an impl involving associated types, such as the FromResidual for Option<T> one in the standard library which desugars to FromResidual<<<Option<T> as Try>::Residual>> for Option<T>, then perhaps, the “convenient” R = <Self as Try>::Residual default type parameter on the FromResidual trait is a bad idea?
I'll still need to test if that actually fixes it, but yes, that was the idea of what I believe could fix it.
The associated tyle I'm referring to is the associated type <Option<T> as Try>::Residual appearing as the type parameter in the impl FromResidual<<<Option<T> as Try>::Residual>> for Option<T>.
I think jump2 is the most readable. Mentally I'm doing a lot more gymnastics to figure out whether jump1 is a conditional return or whether the if expression returns a value that gets returned. If I'm skimming code, I'm much more likely to make a mistake with jump1.
In a language where "if" is a statement and never an expression, this isn't a problem. Rust already deviates a bit with the way it handles implicit returns and such, I'd be very wary about adding another layer of complexity on-top of that.