What does do .... end mean

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
1 Like

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!