rust-lang / rust-lang/rust-clippy

A lint for diverging expressions in else-case to encourage early retuns (and similar)

Open
#13,674 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

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).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the examples in the issue and review the existing if_not_else, nonminimal_bool, and redundant_else lint behavior. Determine the intended scope for diverging expressions in else branches, including if-let-else, and how overlapping suggestions should interact. Done means the lint's behavior and default configuration are settled and its interactions do not produce conflicting transformations.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.