こんにちは!FlutterやDartの開発現場を楽しんでいますか?
他のプログラミング言語からDartの世界に飛び込んできた方から、よくこんな質問を受けるんです。
「Dart 3でパターンマッチングが導入されてすごく便利になったんだけど、switch文やdestructuring(分割代入)で『この値、取り出すけど絶対に使わないんだよね』っていう変数名に困ることがあるんだよね……」って。
使わない変数なのに、適当に `dummy` とか `temp` とか名前をつけて、あとからLinterに「この変数は使われていませんよ(unused variable)」って怒られたり、あるいはそのまま放置してモヤモヤしたり……。そんな経験、ありませんか?
実は、Dart 3にはそのモヤモヤを秒で解消し、さらにコンパイラの最適化すら味方につけられる最高にクールな機能があるんです。それが今回解説する「ワイルドカードパターン(`_`)」です。
ここをクリアすれば、あなたのDartコードは一気に洗練され、モダンで美しいものになりますよ。さあ、一緒にマスターしていきましょう!
—
1. ワイルドカードパターン(`_`)ってなに?
一言でいうと、ワイルドカードパターンとは「ここにある値は受け取るけれど、名前はつけない(=コードのどこからも参照しない)」ということをコンパイラに伝えるための記号(アンダースコア `_`)です。
他の言語(RustやScalaなど)を触ったことがある方なら「おっ、あれか!」とピンとくるかもしれません。Dart 3からは、この `_` がパターンマッチングの文脈において「名前を持たないプレースホルダー」として正式に機能するようになりました。
思考の図解:変数名をつける vs ワイルドカードを使う
通常の変数代入と、ワイルドカードの挙動を脳内イメージしてみましょう。
- 通常の変数(例: `name`)
[ データ構造 ] ──(値を取り出す)──> [ 変数 “name” ] ──(メモリ上に領域を確保し、スコープに保持)
- ワイルドカード(`_`)
[ データ構造 ] ──(値を取り出す)──> [ 捨て場 (Discard) ] ──(メモリに保持しない・名前を生成しない)
ワイルドカードを使うことで、Dart VMやコンパイラは「あ、この値は名前としてバインド(結合)しなくていいんだな」と理解し、不要なシンボルテーブルへの登録やメモリのアロケーションを回避してくれます(※コンパイラの最適化フェーズにおいて、不要なローカル変数のライフサイクル管理コストが消えます)。
—
2. 具体的なコードで使い方をマスターしよう
百聞は一見にしかず。実際のコードを見てみましょう。
例えば、`(String, int)` のようなレコード(Record)型から、「1番目の値(名前)は要らないけど、2番目の値(年齢)だけ使いたい」というシチュエーションを考えてみます。
void processUserRecord(Record userRecord) {
// レコードをパターンマッチで分解する
// 1番目の要素(名前)は使わないので ‘_’ にする!
// 2番目の要素(年齢)には ‘age’ という名前をつける。
switch (userRecord) {
case (_, int age) when age >= 18:
print(‘成人です。年齢は $age 歳ですね。’);
break;
case (_, int age):
print(‘未成年です。’);
break;
default:
print(‘想定外のデータです。’);
}
}
void main() {
processUserRecord((‘Alice’, 25)); // 出力: 成人です。年齢は 25 歳ですね。
processUserRecord((‘Bob’, 15)); // 出力: 未成年です。
}
このコードの `case (_, int age):` の部分に注目してください。
1番目の要素である文字列(`’Alice’` や `’Bob’`)は、パターンマッチの構造としては一致させていますが、変数名を与えていないため、メモリ上に保持されません。
もしここに `case (String name, int age):` と書いて `name` を一度も使わなかったらどうなるでしょう?
DartのLinter(分析ツール)は親切にこう警告してくれます。
> The value of the local variable ‘name’ isn’t used.(ローカル変数 ‘name’ の値が使われていません)
ワイルドカード `_` を使えば、この警告をスマートに回避できます。
—
3. 応用編:オブジェクトの分解やリストマッチングでの活用
ワイルドカードは、レコードだけでなく、クラスのインスタンス(オブジェクト)の分割や、リスト(List)のパターンマッチでも真価を発揮します。
クラスのプロパティを無視する例
class Point {
final int x;
final int y;
final int z;
const Point(this.x, this.y, this.z);
}
void evaluatePoint(Point point) {
// x座標だけが重要で、yとzはどうでもいい場合
switch (point) {
case Point(x: 0, y: _, z: _):
print(‘X軸上の原点を通る、またはY/Zが任意の点です(x = 0)。’);
break;
default:
print(‘その他の点です。’);
}
}
ここで `y: _, z: _` と書くことで、「yとzの存在はチェックする(あるいはマッチさせる)けれど、変数としては取り出さない」という意図が明確になります。コードの読み手にとっても、「あ、ここは無視していい値なんだな」と一目で伝わりますよね。
—
4. 陥りがちな文法エラーと注意点 ⚠️
ここで、初学者が非常によくやってしまう「ある勘違い」についてお話ししておきます。ここが今回の最も重要なポイントの一つです。
❌ 勘違い:「`_` はどこでも使える変数名(プレースホルダー)だ」
例えば、以下のようなコードを書いたことはありませんか?
void badExample() {
var _ = ‘Hello’;
var _ = ‘World’; // ⚠️ エラー!
print(_);
}
「ワイルドカードは名前のない変数なんだから、何度使ってもいいんでしょ?」と思いがちですが、これは間違いです。
Dart 3の仕様において、ワイルドカード `_` は「変数を宣言しない(名前をバインドしない)」ための特別な構文です。そのため、`_` という名前の変数が複数存在することは許されません(同じスコープ内で同じ名前の変数を二重定義することになるため)。
> 💡 正しい理解:
> ワイルドカード `_` は「変数を定義している」のではなく、「変数を定義することをボイコット(拒否)している」のです。したがって、`print(_);` のように `_` を値として参照しようとすると、Dartは「そんな変数はありません」とエラーを投げます。
—
まとめ:ここをクリアすればDartはもっと楽しくなる!
いかがでしたでしょうか?
今回の内容をギュッと凝縮して振り返ってみましょう。
1. ワイルドカードパターン(`_`)を使うことで、不要な変数への代入やメモリの無駄なアロケーションを防げる。
2. Linterの「使われていない変数」警告を美しく回避できる。
3. コードの読み手に対して「この値は意図的に無視している」という明確な意図を伝えられる。
4. ただし、`_` は変数ではないため、後から参照(読み取り)することはできない。
ここをしっかりと押さえておけば、Dart 3のパターンマッチングを完全に手なずけたと言って過제를クリアです。あなたの書くコードは、より堅牢で、よりプロフェッショナルなものに進化していますよ。
日々のコーディングにぜひ取り入れてみてくださいね。それでは、また次回のモダンDartの世界でお会いしましょう!