アーリーリターン(early return)とは?ネストを減らす書き方とガード節・言語別の実装
アーリーリターン(early return、早期リターン)は、条件を満たさないケースを関数の冒頭で return して抜けてしまう書き方です。処理を続ける価値がない入力を先に振り落とすことで、後続のロジックを深い if のネストから解放し、上から下へ一直線に読めるコードにします。この記事では、定義とネスト解消の仕組みをBefore/Afterで示し、Python・JavaScript・Java・Rustでの書き方、ガード節との関係、そして「関数の出口は1つにすべき」という単一return原則との対立点までを整理します。
まとめ
アーリーリターンは、前提を満たさない入力を関数の入口で早めに return し、本来やりたい処理をネストの外側(インデントの浅い位置)に置くための手法です。効果はコードの見た目の変化にとどまりません。ネストが浅くなると「どの条件が満たされたときに、どのコードが動くのか」の対応が追いやすくなり、条件の追加・変更やバグ調査のコストが下がります。この入口チェックを担う return 文をリファクタリングの分野ではガード節(guard clause)と呼び、Python・JavaScript・Java・Rustのいずれでも同じ発想で書けます。一方で、関数の出口が増えることや、ファイル・DB接続などの後片付けが必要な処理では注意が要ります。以下で仕組みと言語別の書き方、使うべきでない場面まで具体的に見ていきます。
アーリーリターンの定義と早期リターン・ガード節の関係
アーリーリターンとは、関数の途中(多くは冒頭)で条件を判定し、その先の処理が不要と分かった時点で即座に return する制御の書き方を指します。「アーリーリターン」「早期リターン」「early return」はいずれも同じ概念を指す表記ゆれで、意味の違いはありません。
混同されやすいのがガード節との関係です。ガード節は、関数の入口で「この条件を満たさないなら、ここで打ち切る」という番人役の return(あるいは例外送出)を指す、リファクタリングの用語です。マーチン・ファウラーは著書『リファクタリング』で、入れ子になった条件分岐を「ガード節による入れ子条件分岐の置き換え(Replace Nested Conditional with Guard Clauses)」として整理しています。つまりガード節はアーリーリターンの代表的な使い方の1つであり、両者はほぼ同じ場面で登場します。本記事でも入口の return をガード節と呼びます。
early returnを使わないコードのネスト深化と可読性の低下
アーリーリターンを使わずに正常系だけを if の内側へ進めていくと、条件が増えるたびにインデントが深くなります。次のPythonコードは、注文データを検証してから処理する典型例です。
def process_order(order):
if order is not None:
if "items" in order:
if len(order["items"]) > 0:
return "受注OK"
else:
return "エラー: 商品が空です"
else:
return "エラー: itemsキーがありません"
else:
return "エラー: 注文がNoneです"
正常系の "受注OK" が4段のネストの最奥にあり、対応する else は遠く離れた場所に散らばっています。条件を1つ足すたびにインデントがさらに1段深くなり、どの else がどの if に対応するのかを目で追う必要が出てきます。ネストの深さは、読み手が同時に頭へ載せておくべき文脈の数に直結し、それが可読性とバグの温床になります。
early returnによる書き換えと得られる効果
同じロジックをアーリーリターンで書き直すと、異常系を先に return で振り落とし、正常系をネストの外へ引き出せます。
def process_order(order):
if order is None:
return "エラー: 注文がNoneです"
if "items" not in order:
return "エラー: itemsキーがありません"
if len(order["items"]) == 0:
return "エラー: 商品が空です"
return "受注OK"
行数はほとんど変わりませんが、読み方が変わります。上から順に「Noneならここで終わり」「itemsが無ければここで終わり」と、失敗条件とその結末が同じ行に並ぶため、対応する else を探す必要がありません。そして最後の return "受注OK" は「ここまでの関門をすべて通過した」ことを意味します。効果を具体的に挙げると次のとおりです。
- 可読性:正常系がインデントの浅い位置に来て、処理の本筋が一目で分かる。
- 変更容易性:新しい検証は
if ...: returnを1行足すだけで済み、既存のネストを組み替えずに拡張できる。 - 調査のしやすさ:どの入力でどのエラーが返るかが1対1で並ぶため、不具合の再現条件を特定しやすい。
コードをシンプルに保つ発想は、KISS原則(KISSの法則)とは?意味・由来とシンプルな設計の実践や、良いコードの書き方|可読性・保守性を高める10の原則と実践例とも地続きです。アーリーリターンは、その原則を制御構文のレベルで実践する具体的な道具といえます。
言語別のearly returnの書き方
アーリーリターンは特定の言語の機能ではなく、return 文を持つ言語であればどこでも使える書き方です。ここではPython・JavaScript・Java・Rustで同じ「割引額を求める関数」をガード節で書き分けます。発想はすべて共通で、無効な user を先に振り落として本処理を末尾に置く点が変わりません。
Python
Pythonは return をそのまま並べます。真偽判定は is None や not で素直に書けます。
def get_discount(user):
if user is None:
return 0
if not user.is_active:
return 0
return user.rank * 100
JavaScript
JavaScriptでは if の本体を波かっこなしの1行にして、ガード節を短く保つスタイルが定着しています。!user は null や undefined をまとめて弾けます。
function getDiscount(user) {
if (!user) return 0; // ガード節:早期に抜ける
if (!user.isActive) return 0;
return user.rank * 100;
}
Java
Javaは戻り値の型が固定されているため、各 return が同じ型(ここでは int)を返す必要があります。void メソッドで途中終了したいときは、値を伴わない return; をガード節に使います。
int getDiscount(User user) {
if (user == null) return 0;
if (!user.isActive()) return 0;
return user.getRank() * 100;
}
Rust
Rustは return による途中脱出に加え、let ... else(1.65で安定化)を使うと「取り出せなければ即 return」というガード節を宣言的に書けます。
fn get_discount(user: Option<&User>) -> i32 {
let Some(user) = user else { return 0; };
if !user.is_active { return 0; }
user.rank * 100
}
さらにRustでは、Result や Option を返す関数で ? 演算子が「エラーなら呼び出し元へ即 return」というアーリーリターンとして働きます。エラー処理のたびに if を書かずに済むのが利点です。次のスニペットは use std::io; と use std::fs; を前提とし、読み込みに失敗した時点で関数を抜けます。
fn read_config(path: &str) -> Result<String, io::Error> {
let text = std::fs::read_to_string(path)?; // 失敗時はここで即return
Ok(text)
}
early returnを使うべきでない場面と注意点
アーリーリターンは万能ではありません。無条件に「return文を前に出せばよい」と考えると、かえって読みにくく、バグを生むコードになります。判断が分かれる論点を具体的に押さえておきます。
単一return原則との対立
「関数の出口は1つにすべき」という単一出口原則(single exit / single return)は、構造化プログラミングの流れをくむ古典的なガイドラインで、いまでも根強く支持されています。出口が1か所ならデバッガでその1行にブレークを張れば必ず戻り値を捕まえられる、という主張です。アーリーリターンはこの原則と真っ向から対立します。
ここでは立場をはっきりさせます。短く前提チェックが中心の関数では、ガード節による複数returnを優先すべきです。単一出口を守るために result 変数と深いネストを持ち込むと、原則の目的だった「追いやすさ」自体を失います。逆に、関数が長く、途中に副作用や後片付けが挟まる場合は、複数returnを避けて出口を集約する方が安全です。目安は「入口の門番として使うなら複数return、処理の途中から飛び出すなら要注意」です。判断に迷うほど関数が育っているなら、それは関数を分割すべきサインでもあります。
リソース解放を伴う処理での早期脱出
ファイル・ソケット・DB接続など、開いたら必ず閉じる必要があるリソースを扱う関数で不用意に return すると、後片付けを飛ばしてリソースリークを起こします。これは各言語が用意した仕組みで防ぎます。Pythonなら with、Javaなら try-with-resources または finally、Go(早期リターンが定石の言語)なら defer を使い、どこで return しても解放が走るようにします。「早期リターンとリソース解放を、後始末の仕組みなしに手書きで両立させない」ことが原則です。
乱用による見通しの悪化
ガード節は入口に集めてこそ効果を発揮します。関数の中盤・終盤に return が点在すると、「この関数はどこで終わるのか」が読めなくなり、単一出口原則が警戒したデバッグのしにくさが現実になります。前提チェックは冒頭にまとめ、本処理の途中からの脱出は最小限にとどめるのが、アーリーリターンを効かせるコツです。if の条件式そのものを読みやすくするには、真偽を表す変数やメソッドの名付けが効きます。その具体的な付け方はプログラミングの命名規則を英語で書く方法で解説しています。
よくある質問(FAQ)
アーリーリターンと早期リターンは違うものですか?
同じものです。「アーリーリターン」「早期リターン」「early return」はいずれも、条件を満たさない場合に関数の途中で return して抜ける同一の書き方を指す表記ゆれで、意味の差はありません。
early returnとガード節の違いは何ですか?
ガード節は、関数の入口で前提条件を満たさない入力を打ち切る return(または例外)を指すリファクタリングの用語で、アーリーリターンの代表的な使い方の1つです。アーリーリターンの方が広い概念で、入口チェック以外の途中脱出も含みます。実務上はほぼ同じ意味で使われます。
Pythonでearly returnはどう書きますか?
関数の冒頭で if 条件: return 値 を並べ、無効な入力を先に返します。if user is None: return 0 のように異常系を先に振り落とし、本来やりたい処理を最後の return に置くのが基本形です。
returnが複数あるのは良くないと聞きましたが本当ですか?
「関数の出口は1つ」という単一出口原則からは複数returnは避けたいとされますが、短い前提チェック中心の関数ではガード節による複数returnの方が読みやすくなります。関数が長く副作用や後片付けを含む場合のみ、出口の集約を検討してください。
early return patternとは何ですか?
early return pattern(アーリーリターンパターン)は、ネストした条件分岐をガード節に置き換えて、正常系をインデントの外へ引き出す設計上の定石を指します。マーチン・ファウラーの「ガード節による入れ子条件分岐の置き換え」がその代表です。