Description
What it does
If you have a return/continue/break/panic or similar in an else case, it's often cleaner to use an early return/continue/break.
This also applies to turning if-let-else into let-else, if the else case always returns/continues/breaks.
I think it might even be one which can be enabled by default. I don't see any cases where it would be difficult to discover this, nor do I see any real drawbacks.
Advantage
- less indentation/nesting
- decreased complexity since else cases are closed
- often considered better style
Drawbacks
Not everybody might prefer early returns.
At least a completely functional style might be preferable.
(but probably almost never a "return" in an else case only)
Maybe the if_not_else
lint might be confusing when not handled in a good way.
Example
fn sum_smaller_than(numbers: &[usize], max: usize) -> Option<usize> {
let mut sum = 0;
for &num in numbers {
if sum < max {
sum += num;
} else {
return None;
}
}
Some(sum)
}
Here's a diverging expression (return) in an else case.
The suggestion would usually be to negate the expression and switch the branches like this:
fn sum_smaller_than(numbers: &[usize], max: usize) -> Option<usize> {
let mut sum = 0;
for &num in numbers {
if !(sum < max) {
return None;
} else {
sum += num;
}
}
Some(sum)
}
Then when following the other lints nonminimal_bool
and redundant_else
lints, we get this:
fn sum_smaller_than(numbers: &[usize], max: usize) -> Option<usize> {
let mut sum = 0;
for &num in numbers {
if sum >= max {
return None;
}
sum += num;
}
Some(sum)
}
Following the if_not_else
lint would just revert it back, so it should be disabled if a redundant_else
lint is discovered for the same lint (probably a separate issue, which is useful by itself).