In Lua, global variables and functions are risky and there are a few crucial things you should understand before you consider creating/adding one to the _G
table
First, let’s quickly discuss the _G
table. In Lua, variables which are all uppercase and prefixed by an underscore (_
) are considered private variables for Lua’s use, for instance _VERSION
. You should not attempt to use these in your program under any circumstances. With that in mind, notice that _G
is also uppercase and prefixed by an underscore! This means that it’s a reserved structure for system use.
It’s true that global variables and functions are convenient. In using them, you can create an object that’s accessible from both main.lua
and any other Lua module, for example a Composer scene. However, it’s important to understand that the following two variables are exactly the same:
myVariable = 10 _G.myVariable = 10
In other words, Lua puts every global variable into the _G
table, and everything you explicitly put in _G
is available by its variable name. For instance:
_G.myVariable = 20 print( myVariable ) --> Prints 20 to the console
So why should you explicity put global variables in the _G
table as in the example directly above? One reason is to clearly identify that you’re declaring a global variable, as opposed to a variable that you meant to be local but simply forgot to prefix with local
. Essentially, prefixing a global declaration with _G.
clearly indicates your intentions when you go back to inspect/modify code.
Some developers have reported that when they print()
variables to the console, Solar2D’s error and warning messages have stopped functioning. The likely cause is that the developer created a global variable named debug
, meaning _G.debug
, and doing so inadvertently “trashed” access to a critical internal library that Solar2D uses to output debug messages.
To inspect this concept further, you can print()
the entire _G
table using the following method:
for k,v in pairs( _G ) do print( k .. " : ", v ) end
Running this will output the full contents of _G
, including:
_G : table: 0x610000468580 _VERSION : Lua 5.1 package : table: 0x600000870300 tostring : function: 0x60000024d290 print : function: 0x60000024c870 lpeg : table: 0x610000463180 os : table: 0x600000a6c240 unpack : function: 0x60000024cf00 lfs : table: 0x610000469e80 require : function: 0x60000067af00 debug : table: 0x600000270a80
All of the APIs listed in the API Reference under the (globals) entry are contained, as well as some undocumented items like debug
and several core API groups like math
, string
, and audio
. With this in mind, you might assume that you can write code like this:
_G.audio = true -- Play audio if true, don't play if false _G.type = "Gold" _G.timer = timer.performWithDelay( 1000, doSomething )
However, all this does is “trash” these internal Solar2D libraries, meaning you won’t be able to play audio or use the type()
function — and that timer isn’t going to time anything!
You may think that the solution is to simply avoid those global names. Not so fast! The list output by the loop above indicates the libraries that you shouldn’t overwrite today, based on an otherwise empty main.lua
file. That list also assumes that you haven’t require()
-d
Another reason why globals are dangerous is that — because they have no visibility limits — they can be expensive to use in regards to performance. In fact, if a global variable is accessed in a
Some developers use — and sometimes abuse — globals as a way to access something they haven’t declared yet, or access variables/functions between modules. Fortunately, both of these tasks are easy to accomplish without using globals!
The first case — accessing something you haven’t declared yet — can be solved by the forward declaration method. As a Lua/Solar2D programmer, understanding scope is essential. If you need to use a variable (usually a function) before you declare it, simply
local doSomething -- Forward declaration local function makeSomethingHappen() local count = 10 doSomething( count ) end doSomething = function( count ) -- Assign function to forward declaration print( count ) end makeSomethingHappen()
On line 8, we simply assign the function which will do the work to the variable doSomething
which was makeSomethingHappen()
function doSomething()
function since it was declared “above” the code block which requests it.
To learn more about Lua scope and the importance of obeying scope rules, please see the Scope for Beginners tutorial.
To solve the second case — accessing variables between modules — you can simply create a Lua file named globalData.lua
(or similar) with the following contents:
-- Pseudo-global space local M = {} return M
Then, in each of your Lua modules, simply require()
the module as follows:
local globalData = require( "globalData" )
From there, setting a “global” variable is as easy as this:
globalData.playerName = "John Smith"
And reading a value from any module — assuming you included local globalData = require( "globalData" )
—
local currentPlayer = globalData.playerName
As you can see, this method lets you use the globalData
table just like you would _G
and circumvent the risk of overwriting things in the _G
table, as well as avoid the general issues and pitfalls that come along with globals.
We’ve always emphasized the “avoid globals!” stance and, as you can see by the methods above, there is essentially no further need for global variables or functions. With careful scoping and use of a