If the value is set before do then wouldn’t it still print the value on the last line instead of nil?
Don’t think of it like that. It is like a for i = 1, 10 do
. The i
is before the do
, but you cannot access it outside of the scope. It is still in the scope.
Confusion…
It is just something to memorize. Think of it this way. All the variables on the same line as do
are local to the do
scope.
local v = true do --they are on the same line
end
print(v) --> nil
local v = true
do --not on the same line
end
print(v) --> true
The do end
is used to create a new scope, the same way curly brackets { }
are used in languages like C and C++
The benefits of creating new scopes is that it provides an easy way to manage memory usage by cleaning up variables you only need to use:
- Once but to fetch a group of separate data
- When a function is called
- Only when an if statement runs
- Only while a loop is running
One example of how you can use do end
is by doing this:
local module1, module2
do
local replicatedStorage = game:GetService"ReplicatedStorage"
module1 = require(replicatedStorage.Module1)
module2 = require(replicatedStorage.Module2)
end
ReplicatedStorage is usually a good candidate to use do end
because it’s a service used to store Instances, so in most cases is only used once to get those Instances then is no longer needed. By doing as the example code, after using ReplicatedStorage to fetch the modules it is automatically set to nil when the do end
block concludes. You can actually test this out by doing this:
do
local a = 123456
end
print(a) -- Will output nil because 'a' doesn't exist in the scope outside the do end block
But why not do something like this instead, wouldn’t it do the same thing?:
local replicatedStorage = game:GetService"ReplicatedStorage"
module1 = require(replicatedStorage.Module1)
module2 = require(replicatedStorage.Module2)
replicatedStorage = nil
True, it will do the same job but it does come with a problem if you use strict type-checking since if you do:
local replicatedStorage: ReplicatedStorage = game:GetService"ReplicatedStorage"
module1 = require(replicatedStorage.Module1)
module2 = require(replicatedStorage.Module2)
replicatedStorage = nil
replicatedStorage = nil
will be underlined with a type error stating: Type 'nil' could not be converted into 'ReplicatedStorage'
. Using do end
in this case would allow you a way to prevent the type error from showing up like so:
do
local replicatedStorage: ReplicatedStorage = game:GetService"ReplicatedStorage"
module1 = require(replicatedStorage.Module1)
module2 = require(replicatedStorage.Module2)
end
Also, here’s a link to the doc article that explains what scopes are which is important to understanding how do end
works:
But this doesn’t print nil?
No, because the v = true
is followed by a do
.
do
local v = true
end
--the above is the same as this:
local v = true do
end
I actually used it practically in my game im working on. Its used to separate “Scopes” basically lets say you have 2 inputservice functions to do different actions on a weapon normally you would need to tename the variables to something like Hold1 Hold2 etc. so instead of doing that you can just wrap then in do end
and separte the scope.
The 200 local limit is per scope. You can define 200 local variables inside a do-end block, and then another 200 local variables inside another do-end block. Could be the same variable names because you’ve already escaped the block and the locals inside of it gets trashed.
But obviously don’t actually make that much variables because it’s literally a pending death sentence.
do
local var1 = ...
local var2 = ...
local var3 = ...
...
local var200 = ...
end
--from here on, the local variables above are all voided and set to nil
do
local var201 = ...
local var202 = ...
local var203 = ...
...
local var400 = ...
end
Oh ok. Haha I won’t, I’m not a messy person (I don’t think)
Those are not the same. It doesn’t matter whether local v = true
is on the same line as do
. The only thing that matters about white space (spaces, new lines, tabs etc.) in lua code is that there is at least one white space character wherever whitespace is needed. The scope of v
depends on the order of its definition and the do
keyword. If it’s defined before the do
keyword, it will exist outside the do end
block. If it’s defined between do
and end
, it won’t exist outside the do end
block.
The following two are equivalent:
do
local v = true
end
do local v = true
end
However, your code
local v = true do
end
Is not equivalent to these.
In the case of a for loop, both the code between do
and end
and the variable defined between for
and do
are in the scope of the loop. So even in that case, being on the same line is not the reason for i
only existing in the loop. You can write the loop this way too:
for
i
=
1
,
10
do
print(i)
end
I just said that because it is easier to understand. However, it appears I was wrong. do
does not make any variables local when on the same line and does not behave similar at all to the for
operator to enclose variables. I must’ve been using it wrong this whole time. Who would’ve thought!