VBA programs execute code-lines in a sequential manner. However, situations often arise where you need to skip ahead or circle back to a code-line. That is where jump statements like the GoSub – Return statement come into play. They are of immense importance as they allow the non-sequential transfer of control (i.e., program execution).
Introducing the GoSub – Return Statement
The GoSub – Return statement is a blast from the past. It comes from the era of BASIC programs, VBA’s ancestor. Those days, you could only modularize programs by splitting procedures into subroutines.
Once broken up, GoSub statements unconditionally transferred control to these subroutines. Then Return statements, at the end of the subroutines, sent it back to the line right after the GoSub statement.
These subroutines differ from the Sub, Function, or Property procedures. Those procedures have opening and closing block declarations. In fact, these subroutines can only exist inside Sub, Function, or Property procedures. They do so as code-lines enveloped by labelled (top) and Return statements (bottom).
The GoSub – Return statement is like the GoTo statement in many ways. One of those is that it must be in the same Sub, Function, or Property procedure as its subroutine and labelled statement.
The header image above illustrates the statement’s syntax. Moreover, the flowchart below shows its logic flow.
Salient Points on Usage
There are several important things to note about the GoSub – Return statement:
- It must be in the same procedure as the labelled statement it jumps control to. So, you can’t use it to get into or out of Sub, Function, or Property procedures. The Call and Exit statements handle those tasks.
- An Exit statement must always follow the GoSub statement. And it should be right before the subroutine’s labelled statement. Otherwise, execution “falls through” the subroutine. That is, the subroutine runs again after its Return statement sends control back to the line after its GoSub statement. When that occurs, the Return statement runs again, after the subroutine’s new run. However, this time it has no GoSub statement to work with as it wasn’t triggered by one. As a result, a “Return without GoSub” runtime error occurs.
It serves as a backward compatibility enabler. So, while its use is perfectly valid, and not necessarily considered bad practice, you should avoid using it. Instead, convert its subroutines into separate Sub, Function, or Property procedures. Then invoke them using Call or Assignment statements.
The GoSub – Return Statement in Action
The sample code below shows the GoSub – Return statement in use. It also illustrates the salient points discussed above.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Option Explicit Public Sub GoSubReturnStatement_Example01() 'demonstrating the GoSub-Return statement's usage Dim byt_Tracker As Byte 'variable declaration line1: byt_Tracker = 2 'initialize tracker GoSub line3 'jump forward to subroutine starting at line3 GoSub line2 'jump forward to subroutine starting at line2 Exit Sub 'prevents fall-through error from preceding GoSubs line2: 'double tracker's value & display result byt_Tracker = byt_Tracker * 2 Debug.Print "Tracker = " & CStr(byt_Tracker) 'to Immediate window Return 'jumps backward to line just after "GoSub line2" line3: 'triple tracker's value & display result byt_Tracker = byt_Tracker * 3 Debug.Print "Tracker = " & CStr(byt_Tracker) 'to Immediate window Return 'jumps backward to line just after "GoSub line3" End Sub |
The Sub procedure in the sample code above does the following:
- Declares a Byte variable, byt_Tracker, to hold a tracking number (code window line #7);
- Sets byt_Tracker’s value to ‘2’ in the labelled statement, line1 (line #9);
- GoSub line3 statement (line #11) skips control to the subroutine starting at the labelled statement, line3 (line #21);
- Triples byt_Tracker‘s value to ‘6’ (line #23);
- Prints the result to the Immediate window using the Debug.Print statement (line #22);
- The Return statement (line #25) jumps control to the line right after the GoSub line3 statement. That would be the GoSub line2 statement (line #12);
- The GoSub line2 statement skips control to the subroutine starting at the labelled statement, line2 (line #15);
- Doubles byt_Tracker‘s value to ‘12’ (line #17);
- Prints the result to the Immediate window using the Debug.Print statement (line #18);
- The Return statement (line #19) jumps control to the line right after the GoSub line2 statement. That would be the Exit Sub statement (line #14);
- The Exit Sub statement, right before both subroutines, immediately ends the Sub procedure.
The Exit statement, right before all subroutines in the Sub procedure, is crucial. Without it, the subroutine at line2 (line #15) would run again, after the tripling and doubling. However, that subroutine’s Return statement throws a runtime error since it was not invoked by a GoSub statement.
The results of the sample code above can also be achieved using the GoTo statement. However, as most people would probably agree, the GoSub – Return statements approach is more elegant.