prefer_early_return
This rule flags a function body that consists of a single if wrapping everything the function does, where inverting the condition and returning early would remove a level of indentation.
Why use this rule
Section titled “Why use this rule”The reader has to hold “we are inside the valid case” for the entire body, and every further condition nests one level deeper. A guard states the precondition once and lets the rest of the function be the happy path.
The whole function body is one if, and the useful work is a level in:
class Order { Order({required this.isPaid});
final bool isPaid;
void reserveStock() {} void printLabel() {} void dispatch() {} void notifyCustomer() {}}
void ship(Order order) { if (order.isPaid) { order.reserveStock(); order.printLabel(); order.dispatch(); order.notifyCustomer(); }}void ship(Order order) { if (!order.isPaid) return;
order.reserveStock(); order.printLabel(); order.dispatch(); order.notifyCustomer();}Known limitations
Section titled “Known limitations”The rule is deliberately narrow. It stays silent in each of these:
A statement before the if. That is setup the guard form would have to move or duplicate:
void ship(Order order) { final startedAt = DateTime.now();
if (order.isPaid) { order.reserveStock(); order.printLabel(); order.dispatch(); }}An else branch. Inverting would swap the branches rather than flatten anything — that case belongs to avoid_negated_conditions.
An already-negated condition. if (!map.containsKey(key)) inverts into a longer positive guard, and the negation is what made the precondition obvious:
void register(Map<String, int> counts, String key) { if (!counts.containsKey(key)) { counts[key] = 0; counts[key] = counts[key]! + 1; print('registered $key'); }}A pattern if. if (x case final int n) binds variables the inverted branch cannot see.
A body shorter than min_statements — three by default. See below.
Enabling this rule
Section titled “Enabling this rule”This rule is in the pedantic preset, so it is enabled by preset: pedantic or by name:
rules: prefer_early_return: enabled: trueOptions
Section titled “Options”min_statements
Section titled “min_statements”How many statements the wrapped block must hold before the guard is worth it. Below that, the rewrite saves no nesting worth doing:
many_lints: rules: prefer_early_return: min_statements: 4rules: prefer_early_return: min_statements: 4With min_statements: 4, a three-statement body stops being reported:
void ship(Order order) { // Not reported at min_statements: 4 — reported at the default of 3. if (order.isPaid) { order.reserveStock(); order.printLabel(); order.dispatch(); }}| Option | Type | Default | Description |
|---|---|---|---|
min_statements |
int | 3 |
How many statements the wrapped block must hold before the guard is worth it |
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: prefer_early_return: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_collapsible_if— Merge nested if statements with &&.avoid_redundant_else— Drop the else when the if branch always exits.prefer_immediate_return— Return an expression directly instead of via a throwaway variable.avoid_unnecessary_return— Remove a barereturn;that ends a void function.