What a redundant else is
An else is redundant when the if branch before it always ends the function with return. Once that return runs, nothing after it in the function executes. The code that follows the if block can only be reached when the condition was false, which is exactly what the else was saying. Wrapping it in else { ... } adds braces and a level of indentation without changing what the function does.
The function in this lesson is negative. It returns n when n is negative and -n otherwise, so the result is never positive:
impl Solution {
fn negative(n: i32) -> i32 {
if n < 0 {
return n;
} else {
return -n;
}
}
}
It is correct, but both branches return, so the else does no work.
Removing it
Delete the else { and its closing brace, and move the second return out of the block. The if keeps its own closing }:
impl Solution {
fn negative(n: i32) -> i32 {
if n < 0 {
return n;
}
return -n;
}
}
The behaviour is identical. With n = 5 the condition is false, the if block is skipped and the function returns -5. With n = -3 the if block returns -3 straight away, and the last line never runs. With n = 0 the result is 0. The two versions give the same answers for all three inputs.
Why it reads better
- The code is flatter. The main path of the function sits at the same indentation as the checks, instead of being pushed into a block.
- The reader sees a guard: "if this case holds, return now." Everything below is the general case.
- The effect grows with the code. A function with several early returns and a nested
elseafter each one soon becomes a staircase of braces. Without theelseblocks it stays a straight list of checks.
The same idea in Rust's own style
In Rust, if is an expression, and the last expression of a function body, written without a semicolon, is its return value. Because of that, the original function can also be written without any return:
fn negative(n: i32) -> i32 {
if n < 0 { n } else { -n }
}
Here the else is not redundant, because it is needed to give the expression a value on both paths. It gives the same results as the version above: 5 becomes -5 and -3 stays -3. The lesson's exercise uses explicit return statements, and with return the else can go.
When the else is not redundant
The trick only works when the if branch really leaves the function. If it falls through to the end of its block, the code after the if runs in both cases, and removing the else changes the behaviour:
fn describe(n: i32) {
if n < 0 {
println!("negative");
} else {
println!("not negative");
}
}
Here the two branches only print, and nothing returns. Take away the else and the second println! would run for every input, including negative ones. Before you remove an else, check that the branch above it always ends with return (or something else that leaves the function, such as break or continue inside a loop).
The lesson's exercise
- Read the function and find the branch that ends with
return n;. Everything after it runs only whenn < 0is false. - Delete the
elsekeyword and its opening brace. - Delete the matching closing brace, so the
ifblock ends right afterreturn n;. - Keep
return -n;as the next line, now at the function's top level, and un-indent it one level.
The tests check only what the function returns, so the cleaned-up version passes exactly like the original.
Key takeaways
- If an
ifbranch always returns, theelseafter it is redundant. - Removing it keeps the behaviour and cuts nesting and braces.
- Do not remove an
elsewhen theifbranch can fall through, because both branches would then run the code that follows. - In Rust, an
if/elseused as the function's final expression, with noreturn, is another clean form, and there theelseis required.
