【入門編】Dartのリストパターンにおける「可変長マッチング」:restパターン(…)の活用術 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!FlutterやDartを使った開発を楽しんでいますか?

他のプログラミング言語、例えばJavaScriptやRust、Pythonなどを触ったことがある方なら、「パターンマッチングって便利だな〜」と感じたことがあるかもしれません。Dart 3で導入されたパターンマッチングは、私たちのコードを劇的に美しく、そして堅牢にしてくれました。

今回はその中でも、リストの先頭や末尾をスマートに抜き出しつつ、残りの要素をごっそりまとめて受け取る「可変長マッチング(restパターン:`…`)」について、深く掘り下げていきましょう。

ここをクリアすれば、Dartのリスト操作の引き出しが一気に広がり、コードを書くのがもっと楽しくなりますよ。一緒にマスターしていきましょう!

—

1. Dart 3のリストパターンと「restパターン(…)」の基本

まずは、リストパターンがどのようなものか、イメージを掴みましょう。

Dartでは、`switch`文や`if-case`文、あるいは変数宣言の代入(destructuring)において、リストの構造をそのままパターンのように当てはめて値を切り出すことができます。

その中で、「要素の数が可変(何個あるかわからない)」な残りの部分を受け取るために使うのが、ドット3つ(`…`)で表されるrestパターンです。

図解イメージ:リストの解体職人

[ ターゲットのリスト ] —> [ 先頭(first) ] [ 残りすべて(rest) ]
[1, 2, 3, 4, 5] —> 1 [2, 3, 4, 5] (restパターン)

コードで見ると、次のように書きます。

void main() {
final numbers = [10, 20, 30, 40, 50];

// 先頭の1つを取り出し、残りを rest 変数にまとめる
// 構文: [ 変数名, …残りを格納する変数名 ]
final [first, …rest] = numbers;

print(‘先頭: $first’); // 出力: 10
print(‘残り: $rest’); // 出力: [20, 30, 40, 50] (これは新しい List になります)
}

非常にシンプルですよね!`first`には最初の要素である`10`が入り、`rest`には残りの要素が新しいリストとして切り出されます。

—

2. 実践的ユースケース:リストの先頭・末尾・中身の動的ハンドリング

実際のアプリケーション開発(例えばFlutterのUIビルダーや、APIレスポンスのJSONパースなど)では、「リストの要素数が可変だが、特定のポジションを処理したい」という場面がよくあります。

restパターンは、先頭だけでなく、末尾や中間など、どこにでも配置できるのが強力なポイントです。

パターンA:末尾の要素を取り出す

「最後の要素だけ特別に処理して、残りはループで回したい」というケースです。

void processLogEntries(List logs) {
// 末尾を last に、残りを earlierLogs に分ける
if (logs case […var earlierLogs, var last]) {
print(‘通常のログ数: ${earlierLogs.length}’);
print(‘最新のエラー/重要ログ: $last’);
} else {
print(‘ログが空です’);
}
}

void main() {
processLogEntries([‘起動’, ‘データ取得’, ‘描画完了’, ‘例外発生!’]);
// 出力:
// 通常のログ数: 3
// 最新のエラー/重要ログ: 例外発生!
}

パターンB:先頭と末尾を同時に剥ぎ取る(サンドイッチ型)

「最初と最後は確定しているけれど、中の要素数は可変」という、パーサーやプロトコル処理でよく使うテクニックです。

void handleProtocol(List packet) {
// 先頭をヘッダー、末尾をフッター、中身をペイロードとして一網打尽にする
switch (packet) {
case [int header, …var payload, int footer]:
print(‘Header: $header’);
print(‘Payload: $payload’);
print(‘Footer: $footer’);
default:
print(‘無効なパケット構造です’);
}
}

void main() {
handleProtocol([0xFF, 1, 2, 3, 0xEE]);
// 出力:
// Header: 255
// Payload: [1, 2, 3]
// Footer: 238
}

このように、パターンの形がそのままデータの構造を表現するため、コードの意図がひと目で伝わります。

—

3. 陥りやすい文法エラーと知っておくべき注意点

非常に便利なrestパターンですが、Dartのコンパイラ(AOT/JIT)が厳格にチェックするため、いくつか「やってはいけない書き方」があります。ここで躓きやすいポイントを押さえておきましょう。

注意点1:restパターンは1つのリストパターンにつき「1つだけ」

「残りの部分」は当然1つしか存在し得ないため、1つの大カッコ `[…]` の中に複数置くことはできません。

void badExample(List list) {
// ❌ コンパイルエラー! restパターン (…) は複数使えません
// final [first, …middle, …last] = list;
}

もし中間を切り出したい場合は、位置を固定するか、複数回に分けてパターンマッチングを行う必要があります。

注意点2:要素数不足によるランタイムエラー(推論とマッチ失敗)

変数の代入(`final […] = …`)でパターンマッチを行う際、リストの要素数がパターンに満たない場合は、コンパイル時ではなく実行時(Runtime)に `StateError` や `FormatException`(MatchError)が発生します。

void main() {
final List emptyList = [];

// 実行時エラー:要素が最低1つ(first用)必要なのに空のためマッチに失敗する
// final [first, …rest] = emptyList;
}

安全に処理したい場合は、変数代入ではなく、`if-case`文や`switch`文を使いましょう。これらはマッチしなかった場合に安全に `else` 節や次のケースにフォールバックできます。

void safeHandle(List list) {
if (list case [int first, …var rest]) {
print(‘安全に処理できました: $first’);
} else {
print(‘リストが空なので安全にスキップします’);
}
}

—

4. まとめ:Dartを掌握する極限の知見

今回はDart 3のリストパターンにおける「可変長マッチング(restパターン)」について解説しました。

  • `[first, …rest]` のように記述し、先頭や末尾を切り出しつつ残りをリストとして扱える。
  • `…` はリスト内のどこ(先頭・中間・末尾)にでも配置可能。ただし、1つのパターンにつき1箇所のみ。
  • 安全性を担保するため、リストの要素数が保証できないコンテキストでは代入ではなく `if-case` や `switch` を活用する。

リスト操作のためにわざわざ `.sublist(1)` を書いたり、インデックスを指定して安全性を気にするボイラープレートコードとは、今日でお別れです。

ここをクリアできれば、あなたのDartコードはより宣言的で、洗練されたものになります。ぜひ、日々の開発に取り入れてみてくださいね!

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