SoFunction
Updated on 2025-03-10

Introduction to global variables and non-global environments in Lua

Today I will talk about two topics - global variables and non-global environments.

As you feel in your heart, the content of global variables is very simple, and the content of not global environment needs to be slightly exercised.

1. The prototype of global variables

In Lua, it is very simple to declare global variables, that is, when defining variables, do not add local to the previous one.

This mysterious global variable is actually a table in essence, which saves all the global variables we create into a table.

And the name of this table is: _G
 
Let's take a look at the code:

Copy the codeThe code is as follows:

-- Define a global variable
gName = "Oh, it's very frustrating";
  
-- Output the value of the variable in three ways
    print(gName);
    print(_G["gName"]);
    print(_G.gName);

The output result is as follows:

Copy the codeThe code is as follows:

[LUA-print] Oops, very frustrated
[LUA-print] Oops, very frustrated
[LUA-print] Oops, very frustrated

We define a global variable gName, so this gName becomes a field of _G.
How about it, it's very simple.

2. Non-global environment

Regarding global variables, no matter where they go or in which language, people always warn: "Don't abuse them, you will bear the consequences."
Perhaps because of this, Lua has a relatively special mechanism: a non-global environment.
I call it a "global variable that doesn't have a global impact".

3. Change the global variable environment of the function—setfenv function

Let's take a look at the following code:

Copy the codeThe code is as follows:

-- Define a global variable
gName = "Oh, it's very frustrating";
  
-- Reset the current global environment to a new table
    setfenv(1, {});
  
-- Output value
    print(gName);

If you run the code now, the output will look like this:
Copy the codeThe code is as follows:

[LUA-print] LUA ERROR: [string "src/"]:107: attempt to call global ‘print' (a nil value)

Why? Unexpectedly, the face print function cannot be found?

This is because we have changed the global variable environment within the current function scope. The global variable is saved in _G by default, while the current global variable is in a new table.

Currently this table is empty, so there are no global variables.
 
The setfenv function is used to change the global environment in a certain function range. In layman's terms, it is to eliminate the _G in a certain function range.
 
The two parameters of the setfenv function represent:

1). The first parameter can be a function that is about to change the environment, or it can be a number. The number 1 represents the current function, the number 2 represents the function that calls the current function, and so on.

2). The second parameter is the new global environment table.
 
4. Keep the original_G

Now even the print function cannot be used, which is very inconvenient for testing. We can do a small action to keep the original _G.

The following code:

Copy the codeThe code is as follows:

-- Define a global variable
gName = "Oh, it's very frustrating";
  
-- Reset the current global environment to a new table
    setfenv(1, {g = _G});
  
-- Output value
    (gName);
  
-- Define a global variable again
gName = "Oh, it's a bit wrong";
  
--Output value again
    (gName);
  
-- Output the original value
    ();

As long as you place _G as a field in the new table when defining a new environment, you can call the original global variable.

Then, the output result is as follows:

Copy the codeThe code is as follows:

[LUA-print] nil
[LUA-print] Oops, it's a bit wrong
[LUA-print] Oops, very frustrated

The output results of the three calls to the function are different:

a. The first time, the global environment has just been reset. At this time, there is only one global variable in the current function, that is g, so the value of gName is nil.

b. The second time, we assign gName again. At this time, it is already in the new environment, so the gName value output next exists.

c. The third time, the output is the value this time. The gName value called through g is the value in the original global environment, so the value of gName is still the original "Oh, it's very frustrating".
 
Actually, what's the use of this? It would be better to use local variables directly.

Indeed, nothing special is seen from this example.

The introduction of knowledge in the book is from shallow to deep, so there is no more in-depth introduction here for the time being. When I see the content later, I will continue to share it with you.

5. Use the __index element method to retain the original _G

Here is another tip to share. I just gave an example to retain _G, but when calling functions such as print, it still needs to be in a form, which is a bit in the way.

We can use __index to solve this problem, as follows:

Copy the codeThe code is as follows:

-- Define a global variable
gName = "Oh, it's very frustrating";
  
-- A table will soon become a new environment
    local newG = {};
    setmetatable(newG, {__index = _G});
  
-- Reset the current global environment to a new table
    setfenv(1, newG);
  
gName = "Stop oh, it's very annoying!";
  
-- Output value
    print(gName);
    print(_G.gName);

We set a meta table for the new table, and the __index meta method of this meta table is _G.

Therefore, when the print field cannot be found in the new environment, it will go to _G to search.
 
The output result is as follows:

Copy the codeThe code is as follows:

[LUA-print] Stop oh, it's annoying!
[LUA-print] Oops, very frustrated

The first time the gName value in the new environment is output, and the second time the gName value in the original environment is output, which has no effect on each other.

6. End

OK, that's all for now about global variables and non-global environments.

Although I don’t feel any effect yet, it doesn’t matter, there will be content about this part later.

Just like __index, it is the foundation and may be mentioned frequently later.