Small question regarding table references

Thanks for taking time to read this, in advance.

I’m wanting to know if making changes to a variable that references a table will affect the table the variable is referencing, or if the variable becomes a separate dynamic value.

local damage = {}

function module:AddDamage(p1, p2, amt)
	local d = damage[p2]; d = d or {}
	d[p1] = (d[p1] or 0) + amt
end

I’m using this function to track the amount of damage players take so as to reward kill credit to the player that dealt the most damage to them. I create the variable ‘d’ as a reference to Player2’s damage log, setting it to a blank table if one doesn’t exist. I then reference Player1’s entry in P2’s damage log and create a new one if one doesn’t already exist, then add the specified amount to the entry.

What I’d like to know is, will making changes to ‘d’ affect the original ‘damage’ table, or do I need to manually set ‘damage’ to ‘d’?

Tables, functions, threads, and (full) userdata values are objects : variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.

Article 2.1 About Values And Types. (Second to last paragraph)

As described inside the Lua Manual Tables never really contain any values (neither does functions nor threads), so when we do

local Damage = {}
Damage[1] = "Hello"

Damage[1] is not really “Hello”, but a pointer to that variable, hence when you edit the value of a table, it changes the value on the reference, thus changing the contents of a table.

Even if you do it indirectly like

local d = Damage
d[1] = "Goodbye"

Damage[1] becomes Goodbye.

However, the Damage table would not contain anything new added to the d table. Hence

d[2] = “Welcome Back”

Does not make Damage[2] into “Welcome back” since Damage[2] doesn’t contain any pointer to the new d[2] and thus remains empty (nil)

EDIT: What I wrote here is completely wrong. The people below described how tables work better than I did.

Proof With Tables And Pointers
local damage = {p1={p2=0},p2={p1=0}}

function AddDamage(p1, p2, amt)
	local d = damage[p2]; d = d or {}
	d[p1] = (d[p1] or 0) + amt
end


for i, p in pairs(damage) do
	for i, d in pairs(p) do
		print(d)
	end
end

AddDamage("p1", "p2", 3)

for i, p in pairs(damage) do
	for i, d in pairs(p) do
		print(d)
	end
end

Returns

0
0
3
0

Proving that even though I didn’t touch damage directly, it still changed the value itself.

As if p2 never existed. Thus proving that a new table wont add new elements to a former table.
2 Likes

I see. Thanks for the detailed reply - always a great help to learn something new.

Make sure to mark the reply as a solution if it has fixed your problem

What? Doing d[2] = "whatever" would still set it in the Damage table. Both Damage and d are a reference to the same table.

local A = {}
local B = A

A[1] = "woah ."
B[2] = "same ."

print(B[1], A[2]) -- woah . same .
1 Like

Writing d = damage[p2] and then d = {} is not the same as writing damage[p2] = {} and then d = damage[p2].

Well, no, not really.

A table is holding the pointers to the variables inside it. Not a pointer to some table.
Hence, when you edit the d[1], it edits whatever is on that pointer which happens to be the same as Damage[1] since we made it like that. However, adding a new variable won’t add the new pointer to some random table elsewhere.

If you copy my proofs you can see this in action.

EDIT: I posted before I checked, but the people above and below are right in how this works (and I am wrong).

1 Like

…? What? I’m not sure you understand that post.

A and B point to the same table which is held at a certain memory address. You can see this address by printing the table (provided it does not have a tostring metamethod in an attached metatable). Editing a table entry in B modifies the table and A sees those changes as well because it points to the same table.

Someone asked a similar question about table references in a thread about passing by reference. I had a lengthy explanation of how it works; feel free to challenge the post if something isn’t agreeable there.

1 Like

I won’t challenge you as I ran a few tests and came to the conjecture that you are correct on this matter. I thought it worked like I described, but apparently it doesn’t. I guess this is what I get for not properly checking things and making conjectures where there are none.

That article was a nice read.

3 Likes