【入門編】Dartのパターンマッチングにおける「ガード句」の評価順序とパフォーマンス – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!Dartの奥深い世界へようこそ。
FlutterやDartでの開発に慣れてくると、「もっとスマートに条件分岐を書きたい!」と思いますよね。

Dart 3で導入されたパターンマッチングとガード句(`when`節)は、まさにその願いを叶えてくれる強力な武器です。
今回は、このガード句が「裏側でどう評価されるのか」、そして「どう書けばパフォーマンスと可読性を両立できるのか」について、優しく、そして本質的なところまで紐解いていきますね。

ここをクリアすれば、あなたのDartコードはワンランク上の洗練されたものになりますよ。一緒にバッチリマスターしていきましょう!

—

1. パターンマッチングとガード句(when)の基本

まずは、「パターンマッチングってなに?」というところから軽くおさらいしておきましょう。
Dart 3の `switch` 式や `case` 文は、単なる「値の一致(ジャンプテーブル)」ではなく、「データの構造そのものを分解して検査する」ことができます。

そこに「さらに細かい条件をつけたい!」というときに登場するのが、ガード句(`when` 節)です。

基本の形を見てみよう

String evaluateScore(int score) {
return switch (score) {
// パターン: 0以上の整数にマッチし、かつガード句の条件を満たす場合
>= 90 => ‘秀’,
>= 70 when score.isEven => ‘良(偶数ボーナス!)’, // ガード句の登場!
>= 70 => ‘良’,
_ => ‘要努力’,
};
}

このコードの `when score.isEven` の部分がガード句です。
「パターン構造が一致したあとで、さらに追加の真偽値(boolean)評価を行いたい」というときに使います。

—

2. ガード句は「どう評価されるのか」?(脳内トレース)

さて、ここからが少し踏み込んだお話です。
複数の `case` と `when` が並んだとき、Dart VMやコンパイラはどのようにこれらを評価しているのでしょうか?

イメージ図で考えてみましょう。

[入力データ: score = 74]
│
▼
┌─────────────────────────┐
│ case >= 90 ? │ ──(No)──┐
└─────────────────────────┘ │
▼
┌─────────────────────────┐
│ case >= 70 ? │ ──(Yes)─┐
└─────────────────────────┘ │
▼
┌──────────────────┐
│ when score.isEven│
└──────────────────┘
│
(74は偶数なので Yes!)
│
▼
「良(偶数ボーナス!)」を返す

ここで非常に重要なルールがあります。
それは、「パターンマッチ(構造の型や値のチェック)が成功したあとに、ガード句(`when`)が評価される」という評価順序です。

1. ステップ1: まず、データがその `case` のパターン(型や範囲など)に合致するかを安価にチェックします。
2. ステップ2: パターンが一致した場合のみ、`when` の後ろにある式(関数呼び出しや複雑な条件式)が評価されます。

この評価順序を知っていると、「なぜこの書き方がパフォーマンス上有利なのか」が見えてきますよね。

—

3. パフォーマンスと可読性を意識したガード句の書き方

初学者のうちや、急いでコードを書いているときに、やりがちな「もったいない書き方」があります。例えば次のようなコードです。

⚠️ アンチパターン:ガード句に重い処理や重複した条件を書く

// 悪い例(イメージ)
sealed class User {}
class PremiumUser extends User { final int points; PremiumUser(this.points); }
class RegularUser extends User { final int points; RegularUser(this.points); }

String handleUser(User user) {
return switch (user) {
// パターンで絞り込めるのに、whenの中で冗長なチェックをしている
PremiumUser(points: var p) when user is PremiumUser && p > 1000 => ‘VIP’,
PremiumUser() => ‘Standard Premium’,
RegularUser() => ‘Regular’,
};
}

このコード、実は少し冗長です。
`PremiumUser(points: var p)` というパターンの時点で、対象が `PremiumUser` であることは確実に保証されています。そのため、ガード句の中であらためて `user is PremiumUser` と書くのは、コンパイラにとっても人間にとっても二度手間(無駄な評価コスト)になってしまいます。

✨ ベストプラクティス:構造分解を活かし、シンプルな条件を右に置く

Dartのパターンマッチングの恩恵を最大限に受けるには、「構造の分解はパターン側に任せ、ガード句は純粋な追加条件(数値の比較やフラグ判定など)に徹する」のが鉄則です。

String handleUserOptimized(User user) {
return switch (user) {
// パターンで型とプロパティ(p)を同時に取得し、ガード句はシンプルに保つ
PremiumUser(points: var p) when p > 1000 => ‘VIP プレミアム’,
PremiumUser() => ‘通常プレミアム’,
RegularUser() => ‘レギュラー’,
};
}

これなら、コードがスッキリして読みやすいだけでなく、Dart VMにとっても無駄な型チェック(`is` 演算子など)をスキップできるため、実行時のパフォーマンス向上につながります。

—

4. 陥りやすい文法エラーとその対策

ガード句を書くとき、初心者がよくつまずくポイントがいくつかあります。ここで事前にチェックしておきましょう。

エラー1: `when` の位置を間違える

ガード句は、必ず `case`(またはアロー `=>` の前)のパターンの後ろ に記述する必要があります。

// ❌ コンパイルエラーになる例
switch (x) {
when x > 0 => ‘正の数’, // caseやパターンがないのでエラー!
}

// ⭕️ 正しい書き方
switch (x) {
var val when val > 0 => ‘正の数’,
}

エラー2: ガード句の結果が `bool` 以外になっている

`when` の後ろに続く式は、必ず `bool` 型を返す式 でなければなりません。

// ❌ コンパイルエラーになる例(整数を返してしまっている)
switch (x) {
int n when n + 1 => ‘値’, // n + 1 は int型なのでエラー!
}

// ⭕️ 正しい書き方
switch (x) {
int n when (n + 1) > 10 => ‘値’, // 比較演算子でboolにする
}

—

まとめ

いかがでしたでしょうか? 今回のポイントをギュッとまとめますね。

  • ガード句(`when`)の評価順序: パターン(型や構造)が一致した後に評価される。
  • パフォーマンスの極意: 型チェックなどはパターン側に任せ、ガード句はシンプルな条件判定(`bool`式)に絞ることで、無駄な評価をなくせる。
  • 文法上の注意: 必ずパターンの後ろに書き、結果が確実に `bool` になるようにする。

この仕組みを頭の片隅に置いておくだけで、複雑なビジネスロジックを扱うときのコードの美しさとパフォーマンスが劇的に変わります。ぜひ、次のFlutterやDartのプロジェクトで試してみてくださいね。

あなたのDartマスターへの道を、これからも応援しています!

タイトルとURLをコピーしました