Question about tables inside functions

Hello! I would like to ask if I call a function that has a local table inside it and call it again, will the elements from the first calling still be around or will it only include the elements from the second calling? I honestly don’t know how to explain it in a clear way so I will send the code I made here:

function collectPlayers() 
    local pos1,pos2 = (area.Position - (area.Size / 2)),(area.Position + (area.Size / 2))
    local region = Region3.new(pos1,pos2)
    local playersFound = game.Workspace:FindPartsInRegion3(region, nil, math.huge) 
    local tplist = {}
	
	for _, v in pairs(playersFound) do
		local pl = v.Parent:FindFirstChild("Humanoid")
		if pl then
			local player = game.Players:GetPlayerFromCharacter(pl.Parent)
			if not table.find(tplist, player) then
				table.insert(tplist, player)
			end
		end
	end
	return tplist 
end

local TeleportService = game:GetService("TeleportService")
local gameID = 0

function teleport() 
	local players = collectPlayers()
	TeleportService:TeleportPartyAsync(gameID, players) 
end

Here’s what happening and what I’m confused about:

  1. Let’s assume that function teleport() is called and the local variable “players” calls for collectPlayers().

  2. Inside the collectPlayers() function, a table named “tplist” is inserted with a list of players (let’s call them Group A)

  3. Then, collectPlayers() returns with Group A inside table tplist.

  4. After the teleport function() is completed, it is called again. The local variable “players” calls for collectedPlayers() a second time.

  5. Inside the collectPlayers() function, the table is inserted with another list of players (let’s call them Group B)

  6. My question is, once tplist is returned what will be the table’s contents?

     Option A = {Group A}
     Option B = {Group B} -- this is what I want to happen
     Option C = {Group A + Group B} -- this is what I think will happen
     Option D = Error
    

I worry that Option C will happen because this can cause a major bug in my game and then slow down the game’s memory. I hope to hear from you all!

2 Likes

After calling collectPlayers for the 2nd time, before returning, tplist contains “Option B”, or group B. Once the function returns, tplist is most likely nil. If there is a variable with the same name in the surrounding scope, then that variable will be unaffected.

Since you define tplist as a local variable inside collectPlayers, it goes out of scope when the function returns. Read more here, especially the 3rd code example on the page.

1 Like

This actually makes sense but assuming that calling collectPlayers() for the 2nd time returns nil, doesn’t it mean that calling it the first time would also return nil?

Put simply the variable is local to the function so will be created each time the function runs. ie a new table each time. It would only return a table with the players or an empty table.

3 Likes

Oh, I kind of get how it works now! But will the 1st table created be GCed or will it be stuck in the memory?

No, it returns group B the 2nd time (and group A the 1st time), but the variable called tplist is out of scope once the function returns, meaning that if you try referring to it, you get nil.

1 Like

As all strong strong references are gone it would be gc.

edit
You might be intrested in looking at weak tables as it explains some aspects of the gc. GC is a very large topic in general XD

1 Like

Oh, I fully understand now! Thank you very much for helping me out. :))

Oh, thank you for the help! I’ll be searching more on strong references to avoid any memory leaks!

Another tip (I found this out not too long ago)
" __mode Used in weak tables, declaring whether the keys and/or values of a table are weak. NOTE: References to Roblox instances are never weak. Tables that hold such references will never be garbage collected."
Ref Roblox Metatables

1 Like

Basically this means that if a table has a reference to an instance, it will never be GCed?

1 Like

So it seems o.o

Ahh found it not sure if it will help you but be mindful that just because you use :Destroy() it does not mean the memory has been released.

2 Likes

Ohh, I will definitely take note of this in mind. Thank you very much for the tip!

1 Like

I’d like to clarify that this is only the case with tables you currently have a reference to, not for every table you make.

local part = Instance.new("Part")
local tab = {part}

part:Destroy()

Will prevent part from being released effectively forever as we never lose the reference to tab. Whereas in this example

local function f()
    local part = Instance.new("Part")
    local tab = {part}

    return tab
end

f()[1]:Destroy()

Here we do something similar but we drop the reference to the entire table as we index f() directly instead of assigning it to a variable. While tab still contains a strong reference to part, and so would not let part be released, there isn’t a strong reference to tab anymore (or indeed any reference) so the gc will happily collect the entire table including roblox objects held inside.

3 Likes

So if I understand this properly, to effectively cut a strong reference between an instance and a table, you need to remove the instance from the table rather than directly remove the instance? I might be wrong though since I’m really new to tables.

When you have an instance in a table you want to destroy, you must also remove the instance from the table like so.

local part = Instance.new("Part")
local tab = {}

tab.p = part

part:Destroy()
print(tab.p) --part still exists
tab.p = nil
print(tab.p) --every reference is dropped so part is now fully destroyed

Or, if you use it as a key not a value.

local part = Instance.new("Part")
local tab = {}

tab[part] = true

part:Destroy()
print(tab[part]) --part still exists
tab[part] = nil
print(tab[part]) --every reference is dropped so part is now fully destroyed

Hope this clears things up.

2 Likes

Oh, I get it now! I’ll be keeping this info for future references.