# Goto Statement  wiki

A `goto` statement was added in Lua 5.2.0-beta-rc1   and refined in 5.2.0-beta-rc2 . This is a restrictive form of `goto` in that

• A label is visible in the entire block where it is defined (including nested blocks, but not nested functions).
• A goto may jump to any visible label as long as it does not enter into the scope of a local variable. 

This rest of this page explores some usages of this new construct.

### Nested break

```-- 5.2.0-beta-rc2
for z=1,10 do
for y=1,10 do
for x=1,10 do
if x^2 + y^2 == z^2 then
print('found a Pythagorean triple:', x, y, z)
goto done
end
end end end
::done::
```

### Continue or nested continue

```-- 5.2.0-beta-rc2
for z=1,10 do
for y=1,10 do
for x=1,10 do
if x^2 + y^2 == z^2 then
print('found a Pythagorean triple:', x, y, z)
print('now trying next z...')
goto zcontinue
end
end end ::zcontinue:: end
```

### Perl style redo 

```-- Lua 5.2.0-beta-rc2
for x=1,5 do ::redo::
print(x .. ' + 1 = ?')
if y ~= x + 1 then goto redo end
end
```

### Pythonic for-else 

```-- Lua 5.2.0-beta-rc2
for _, x in ipairs(t) do
if x % 2 == 0 then
print 'list has even number'
goto has
end
end
print 'list lacks even number'
::has::
```

```-- Lua 5.1 equivalent
local has
for _, x in ipairs(t) do
if x % 2 == 0 then
has = true
break
end
end
if has then
print 'list has even number'
else
print 'list lacks even number'
end
```

### State machine or Markov chain

```-- 5.2.0-beta-rc1
::a::
print 'A'
if math.random() < 0.3 then goto c end
::b::
print 'B'
if math.random() < 0.5 then goto a end
::c::
print 'C'
if math.random() < 0.1 then goto a else goto b end
```

### Simulated tail call

Lua already has ProperTailRecursion, but in the hypothetical case it did not, we could simulate tail calls with `goto` (as some of the C source code of Lua does):

```-- 5.2.0-beta-rc2 - factorial with tail recursion simulated with goto's
-- (warning: there's no need to do this)
function fact_(n, ans)
::call::
if n == 0 then
return ans
else
n, ans = n - 1, ans * n
goto call
end
end
print(fact_(5, 1)) --> 120
```

### Error handling and cleanup

```-- 5.2.0-beta-rc2
function f()
if not g() then goto fail end
if not h() then goto cleanup_g end
if not i() then goto cleanup_h end
do return true end    -- need do/end?

::cleanup_h::
undo_h()
::cleanup_g::
undo_g()
::fail::
return false
```
(from )

### Switch statement

Not possible without computed `goto` . See also SwitchStatement.

### Conventions for Labels

It may help readability for label names to indicate the direction (up or down) that they jump.  In the example below, it may be conventionally understood that the names `continue` and `skip` will jump down and the name `redo` will jump up.

```-- 5.2.0-beta-rc2
::redo:: for x=1,10 do for y=1,10 do
if not f(x,y) then goto continue end
if not g(x,y) then goto skip end
if not h(x,y) then goto redo end
::continue::
end end ::skip::
```

If you use two such chunks of code in the same scope, you will need to disambiguate the label names (e.g. `@redo1:` and `@redo2:`) or wrap each in a `do/end` block.

### Scoping

Here's some examples of the scoping rules for `goto`:

```::a::
goto b  -- valid (forward jump)
goto a  -- valid (backward jump)
::b::
goto c  -- invalid (jump into nested block prohibited because nested label not even visible here)
goto d  -- invalid (jump into nested function prohibited because nested label not even visible here)
do
::c::
goto a  -- valid (backward jump out of nested block)
goto e  -- valid (forward jump out of nested block)
end
(function()
::d::
goto a  -- invalid (jump out of nested function)
end)()
do ::e:: end  -- valid, but not visible outside the block; above "goto e" sees only next line
::e::  -- valid
goto f  -- invalid (forward jump into scope of local definition)
local x
::f::
goto e  -- valid (backward jump across local definition)
--::e::  -- this would be invalid (duplicate label in same scope)
```

Note that you can think of

```do
<...>
--::a::
goto a  -- invalid (forward jump into scope of local definition)
goto b  -- valid (jump out of block)
<...>
local x
<...>
::a::
<...>
--goto a
::b::
end
```

as equivalent to

```do
<...>
--::a::
goto a  -- invalid (jump into nested block prohibited because nested label not even visible here)
goto b  -- valid (jump out of block)
<...>
do
local x
<...>
::a::
<...>
--goto a
end
::b::
end
```

so, in a way, the rule against "jump into scope of local definition" is implied by the rule against "jump into nested block" (but not vice-versa). However, 5.2.0-beta-rc1 doesn't treat scoping between these two forms exactly analogously: if you add another `::a::` before the `goto a`, the former form will generate an error about duplicate label, whereas the latter will not (though it does in rc2) because the nested `::a::` is never seen by a `goto` outside the nested block (and any `goto` inside the nested block will only see the nested `::a::`).

The particular treatment of labels at the end of the block (`::b::`) is what allows a loop continue construct to be implemented (example above) even when the loop block contains locals following the continue.

### Efficiency

`goto`'s can sometimes generate the exact same bytecodes and debuginfo as control structures, except for `for` loops:

```-- compare.lua
-- tested 5.2.0rc1

local FS = require 'file_slurp'
-- https://raw.github.com/gist/1325400/0de9b965af138f2fb3d76fc81d97a863f6f409b3/file_slurp.lua

local function compile(code)
FS.writefile('luac -o luac.out -', code, 'p')
return binary, text
end

local a, at = compile [[
local x = 1
while not(x > 1e8) do
x = x + 1
end
]]
local b, bt = compile [[
local x = 1
::a:: if x > 1e8 then goto e end
x = x + 1
goto a; ::e::
]]
assert(a == b)
assert(at == bt)

local a, at = compile [[
if x then
f()
else
g()
end
]]
local b, bt = compile [[
if not x then goto a end
f()
goto b; ::a::
g()
::b::
]]
assert(a == b)
assert(at == bt)

local a, at = compile [[
local sum = 0
for i=1,1E8 do
sum = sum + i
end
]]
local b, bt = compile [[
local sum = 0
local i=1; ::a:: if i > 1E8 then goto b end
sum = sum + i; i=i+1
goto a; ::b::
]]
assert(a ~= b) -- these differ significantly and the latter is about twice as slow.
assert(at ~= bt)

print 'DONE'
```

(In the earlier 5.2.0beta, when a single goto exists inside a conditional block, some of the JMP's were superfluous ).

### Changes

In 5.2.0-beta-rc1, labels used the syntax `@name:` (optionally with spaces, e.g. `@ name :`). Restrictions on duplicate label names were different.

RecentChanges · preferences
edit · history
Last edited February 2, 2014 8:27 am GMT (diff)