TL;DR —
GoTo labelは、同じ プロシージャの中でlabel:と印を付けた行へ、条件なしで実行を飛ばし ます。現代の VBA でこれがまだ居場所を持つ場面はちょうど一つ、エラー処理のためのOn Error GoToです。そ れ以外のすべて — ループ、飛ばし、分岐 — では、ジャンプは構造化された道具(If、Exit For、Exit Sub、Do)よりも追いにくく、ループの内や外へ飛ぶとループを静かに壊します。古いコードを読めるように構文は覚え、 新しいコードを書くときは構造化された書き方に手を伸ばしましょう。
Sub GoToBasics()
Dim x As Long
x = 5
If x > 0 Then GoTo Positive
Debug.Print "zero or negative"
Exit Sub ' 下のラベルへ落ち込む前に止める
Positive: ' ラベル - 名前のあとにコロン
Debug.Print "positive"
End Sub
GoTo は、存在するなかで最も古い制御フローの道具です。名前を付けたラベルへ、無条件に、あなたが選んだ一方向
へ実行を移します。VBA は今もこれをサポートしており、現代的な機能の一つ — On Error GoTo — はこの上に築か
れています。ですが普通のフローでは、ジャンプは上から下へは読めないコードを作ります。だからこそ、新しいコー
ドの GoTo はほとんどすべて、代わりに Exit か If であるべきなのです。
この記事で学べること
- ラベルとジャンプの構文 — ラベルの書き方と、
GoToがそこへ届く仕組み GoToが今日でも本当に居場所を持つ唯一の場面 —On Error GoToによるエラー処理- ループの 内 や 外 へ飛ぶと、なぜループカウンターが壊れ、仕事が中途半端に残るのか
- VBA にない
ContinueをまねるためにGoToがどう誤用されるか、そしてきれいな代替手段 - 別のプロシージャのラベルへ
GoToできない理由 - どんなジャンプよりも読みやすい、構造化された代替手段
考え方の軸:無条件・一方通行のジャンプ
ラベル は、名前のあとにコロンを付けて一行に単独で置いたものです。GoTo label は、そこへ実行をまっすぐ
移します。
GoTo CleanUp ' CleanUp: と印を付けた行へ前方(または後方)へ飛ぶ
' ... ここにある行はすべて飛ばされる ...
CleanUp: ' <-- 実行はここから再開する
二つの性質が、GoTo を同じだけ強力で危険なものにしています。それは 無条件 です — それ自体は常に飛ぶの
で、分岐させるには If で包む必要があります — そして、跡を残さない 一方通行 の移動です。制御がどこから
来たかを示すものは何もないので、コードを追う読み手は、ラベルに着地しうるすべての GoTo を見つけるためにプ
ロシージャ全体を走査しなければなりません。Exit For のような構造化された文は、制御がどこへ行くか(Next
のすぐ先)を正確に教えてくれます。GoTo はどこからでも来られます。その違いこそが、GoTo に反対する論拠の
すべてです。
GoTo が居場所を持つ唯一の場面:On Error GoTo
ここに例外があり、それは本物です。VBA のエラー処理は GoTo の上に築かれて います。On Error GoTo は、実
行時エラーが起きたときにどのラベルへ飛ぶかを VBA に伝えます。これは慣用的で、期待されており、この言語には構
造化された代替がありません。
Sub SafeOpen()
On Error GoTo Failed
Workbooks.Open "C:\reports\data.xlsx"
Exit Sub
Failed:
MsgBox "Could not open the file."
End Sub
Failed: ラベルの前にある Exit Sub に注目してください — これがないと、何も問題が起きていなくても、通常の
経路がエラーハンドラーへ 落ち込んで しまいます。この落ち込みは、GoTo とラベル付きコードにまつわる古典的
なバグで、エラーハンドラーに限らずすべてのラベルに当てはまります。仕組みの全体は
VBA On Error を参照してください。On Error の外では、GoTo は道具ではなく警告サ
インです。
落とし穴:ループの内や外へ飛ぶ
GoTo でできる最悪のことは、For や Do ループの境界をまたいで飛ぶことです。GoTo でループの 外 へ飛
ぶと、ループの通常の出口を素通りします — カウンターは保持していた値のまま残され、ループが終わらせるはずだっ
たことは飛ばされます。ループの途中へ 中に 飛び込むと、結果はもっと悪くなります。カウンターが一度も初期化
されていないので、動作は未定義です。
' これをしてはいけない - GoTo でループの外へ飛ぶ
For i = 1 To 100
If Cells(i, 1).Value = "X" Then GoTo AfterLoop ' ループのクリーンな出口を飛ばす
Next i
AfterLoop:
上のコードはたまたま動きますが、正直な書き方は Exit For で、これはループを本来のドアから出て、意図をはっ
きりさせます。「ループの外へ飛ぶ」はどれも Exit For か Exit Do と書いたほうがよ
く、「今すぐプロシージャを出る」はどれも Exit Sub と書いたほうがよいのです。これらは自分が出る構造を名指
しし、制御を予測できる場所に着地させます — GoTo はそのどちらもしません。
GoTo で Continue をまねる — そしてきれいな代替手段
VBA には次のループの繰り返しへ飛ぶ Continue 文がないので、古いコードはしばしば、Next の直前にラベルを置
いてそこへ飛ぶことで、それを模倣します。
' GoTo で「continue」するやり方 - 動くが、これはジャンプ
For i = 1 To n
If Cells(i, 1).Value = "" Then GoTo NextRow
' ... 行を処理する ...
NextRow:
Next i
これは動きますが、ループを理解するために読み手をあちこち飛び回らせます。構造化された書き方は、単に条件を裏
返して、飛ばされる繰り返しが何もせず Next へ抜け落ちるようにします。
' 構造化されたやり方 - ラベルなし、ジャンプなし
For i = 1 To n
If Cells(i, 1).Value <> "" Then
' ... 行を処理する ...
End If
Next i
動きは同じで、まっすぐ下へ読めます。飛ばす条件が一つの判定なら、If が常に勝ちます。Next の前にラベルを
置くパターンは、飛ばす処理が一つの If には込み入りすぎている、まれな場合のために取っておきましょう — そし
てその場合でも、本体を専用のプロシージャに分けることを検討してください。
プロシージャをまたいで GoTo はできない
ラベルは、それが存在するプロシージャに閉じています。GoTo が飛べるのは 同じ Sub または Function
の中のラベルだけで、あるプロシージャから別のプロシージャへは飛べません(VBA はコンパイルエラー
Label not defined を出します)。別のプロシージャへ飛び込みたくなったなら、あなたが本当にしたいのはそれを
呼ぶ こと — MyCleanup あるいは Call MyCleanup — であり、これはあとで制御をあなたに返してくれます。まさ
にジャンプにはできないことです。この制限は一つの美点です。すべてのジャンプを、読める一つの単位の中に閉じ込
めてくれます。
ジャンプが本当に一番ましな選択肢になるとき
正直に言えば、一つだけ譲るべき点があります。深くネストしたループ から抜けることは、外側のループの先にあ
るラベルへの GoTo が代替手段よりきれいになりうる、唯一の制御フローの場面です。VBA は一度に一段より多くの
ループを Exit できないからです。それでもここでさえ、ネストしたループを Function に切り出して
Exit Function を使うほうが、たいていは分かりやすいのです。目安はこうです。GoTo に手を伸ばしていて、それ
が On Error GoTo でないなら、いったん止まって、Exit や If、あるいは小さなヘルパープロシージャがジャン
プなしで同じことを言えないか、自問してください。百回のうち九十九回は、言えます。
ExcelMaster の活用
GoTo は書くのは簡単で読むのは難しく、これはまさに、半年後には誰にも保守できないマクロを生む組み合わせで
す。噛みついてくる間違いは二つ、手前の Exit Sub が抜けているときにラベルへ落ち込むことと、カウンターと仕
事を未定義の状態に残すループ境界をまたいだジャンプです。
ExcelMaster は、上から下へ読めるフローを書
きます。ループが止まるべきところには Exit For、プロシージャが切り上げるべきところにはガード節、そして
On Error GoTo は本来の居場所 — エラー処理 — だけに、通常の経路をハンドラーの外に保つ Exit Sub を添え
て。ブックとコードはあなたの手元に残ります。
よくある質問
VBA の GoTo は何をしますか?
GoTo label は、同じプロシージャの中で label: と印を付けた行へ、無条件に実行を移します。ラベルは、名前の
あとにコロンを付けて一行に単独で置いたものです。GoTo は常に飛ぶので、分岐させるには If で包む必要があり
ます。たとえば If x < 0 Then GoTo Negative のように。実行はそこから、ラベル以降を続けます。
VBA で GoTo は悪い書き方ですか?
普通の制御フローでは、はい — ジャンプは構造化された文よりも追いにくく、ループの内や外へ飛ぶとループカウンタ
ーを壊すことがあります。重要な例外は On Error GoTo で、これは実行時エラーをハンドラーへ導く、慣用的で期待
されるやり方です。古いコードを読むために GoTo を覚え、新しいコードを書くときは Exit、If、Do ループ
を好みましょう。
GoTo のためのラベルはどう書きますか?
名前のあとにコロンを付けて一行に単独で置きます。たとえば CleanUp: のように。そして GoTo CleanUp でそこ
へ飛びます。ラベルは大文字小文字を区別せず、プロシージャに閉じています。プロシージャの末尾にあるラベルの前
には、必ず Exit Sub を置いてください。さもないと、ジャンプするつもりがなくても、通常の経路がラベル付きの
コードへ落ち込んでしまいます。
GoTo は別の Sub や Function へ飛べますか?
いいえ。GoTo が届けるのは同じプロシージャの中のラベルだけで、別の Sub や Function へ飛び込むのはコン
パイルエラーです。別のプロシージャのコードを実行するには、名前で呼びます — 呼び出しは終わるとあなたに制御を
返します。これはたいてい欲しいことであり、ジャンプには決してできないことです。
GoTo を使わずにループを抜けるには?
For ループには Exit For を、Do ループには Exit Do を使います — どちらもループを本来の出口から出て、
その次の行から続きます。ループを抜けるのではなく繰り返しを一回だけ飛ばすには、本体を If で包んで、飛ばす
周回が何もしないようにします。全体像は VBA Exit For を参照してください。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-24。
関連ガイド: VBA Exit For · VBA End · VBA On Error · VBA For Loop · VBA If Then Else
