Pass by reference?

While the Wiki’s explanation is pretty average the “Code” sample part is way too short.

I’m pretty sure quite a lot of people are confuse about this concept and would like to understand it more.

Tables can be used for a range of very complex and advance things and the Example provided doesn’t cover that usage area.

What about Tables within a Table what happen to those when you assign them are they also “Pass by reference” for example?

Can someone craft a better Explanation and Code Sample that was provided to us?

2 Likes

Think of a reference like a notecard with a street address for a house on it. Now, instead of passing the whole house around, you just pass around the notecard with the address on it. When you want to access the house, you have the address at hand. You have a reference to the house.

In Lua, passing by reference vs. passing by value is implicit. Functions, tables, and userdata are passed by reference, while strings, numbers, and booleans are passed by value (e.g. they’re copied).

Here’s a code example:

local myTable = {} -- 'myTable' is technically a reference (i.e. pointer) to the actual table

-- Passing the table to the 'SetValue' function passes it by reference, which allows it to edit the
-- actual original table, not just a copy of it.
function SetValue(tbl, key, value)
   tbl[key] = value
end

-- Pass 'myTable' to be changed by the function:
SetValue(myTable, "Hello", 32)

-- Print out the new value that the function has set:
print(myTable.Hello) --> 32

As you can see, the SetValue function received a reference to myTable, and thus it was able to actually edit the original table. If it wasn’t by reference (and thus was passed by value), then the whole table would have been copied to the SetValue function and it would have been editing a copy instead of the original.

Again, think of it like the notecard with the house address. SetValue's ‘tbl’ variable is like a notecard with the house address of the passed table. And therefore, when you edit the table, it’s able to go to the actual address and mess with that table.


Another real-life example: Phone numbers! Your phone number is kind of like a reference to your phone. When you want others to contact you, you don’t just give them copies of your actual phone! Instead, you give them a reference to your phone (your phone number). Now others can contact your actual phone.

16 Likes

Great explanation that makes it a lot easier to understand Thank you!

Right, I didn’t even realized that Functions, and Userdata are pass by reference.

Is there a list that categorize pass by reference and pass by value?



1 Like

Additionally, here’s a super simple explanation in picture form.

image

When passed by reference, filling the cup fills the original cup (because it’s a reference). When passed by value, it doesn’t.

4 Likes

I don’t know. But typically in most popular dynamic languages, non-primitives are passed by reference, and primitives are passed by value.

The primitives in Lua are:

  • number
  • string
  • boolean
  • nil

The non-primitives:

  • function
  • userdata
  • thread
  • table
3 Likes

Lua strings are GCable objects which means they’re passed by reference. They’re just “immutable” so it’s not apparent, but no copies are made.

That’s right. I believe they’re interned too IIRC. But that’s pretty standard for most languages.

1 Like

I’ve decided to revisit this Thread after discovering this behavior;

Code
local Table1 = {
	Name = 'Table1',
	Array = {1,'A',true,print}
}

local Table2 = {
	Name = 'Table2',
	Array = Table1.Array
}

Table2.Array[1] = 5
print(Table1.Array[1])
-- Expected Output | 5
-- Output | 5

Table2.Array = 'Array'
print(Table1.Array[1])
-- Expected Output | Array
-- Output | 5

print(Table1.Array,Table2.Array)
-- Expected Output | Array, Array
-- table: 0000020713DFDA60, Array

Function Sample
local function Func1()
	
end

local Func2 = Func1

print(Func1,Func2)
-- Output  function: 0000020715949580, function: 0000020715949580

Func2 = 'A'
print(Func1,Func2)
-- Output  function: 0000020715949580, A

I thought Table2.Array = ‘Array’ would also assign ‘Array’ to Table1.Array

I do not recognize this as a bug but I require more understanding to it’s behavior and logic



No. Why would it? You’re reassigning the Array variable of Table2. The actual Array in memory remains untouched and is still pointed towards by Table1.Array. This code is working as expected. You’re only changing what Table2.Array points toward, which is now a string.

The outputs you’re getting are expected.

You are correct and I agree, I understand that it is expected but I was using the term “Expected Output” as of what I’ve expected rather than what it’s suppose to Output.

Although it’s behavior and logic is somewhat confusing which I require an explanation for more understanding towards it.


To my current understanding;

  • Table1 holds a reference to itself and Table2 holds a pointer to Table1
    While I can edit Table1 with Table2 I can’t assign a value to Table1 via Table2


When you write {} in your code, this is acknowledged as constructing a table. Table1.Array constructs a table consisting of four values. This table is stored in memory with the address of 0000020713DFDA60 and Table1.Array points to this memory address, which is the table you just constructed.

Table2 comes along and has an Array value as well. Table2.Array points to Table1.Array, which is pointing towards a memory address (hence pass by reference). Both of these variables now point towards the same address.

When you set a new key via Table2.Array[1] = 5, you’re setting the indice of 1 in the table as 5. This change is pushed to the table, which then any variable pointing towards that table also receives the change. Any variable that points towards address 0000020713DFDA60 sees indice 1 as 5 now.

When you change Table2.Array to a string (Array), you’re reassigning what Table2.Array is referencing. It’s no longer pointing to a table in memory, it’s pointing to a string. The actual table doesn’t get changed at all - it stays there in memory under the same address and Table1.Array still points to it. That is why printing Table1.Array[1] still prints 5, but printing Table2.Array will give you “Array”.

When you print what both Table1.Array and Table2.Array are pointing towards, one continues to point towards the table in memory while the other one points to a string. One of them comes out as the memory address for the table and the other is the string you set Table2.Array to.

When you change what a variable is assigned to, you don’t change the table at all, only what its value is meant to be. Changes to a table from a variable are actually changing the table that’s held at the memory address, which variables referencing the table point to.


As for your little summary you added on, I’ll address the two points you posted as sort of a tl;dr to this thread and a bit of a repeat to what I said.

This is incorrect. Using {} creates a new table in memory at an address, where printing the table gives you that memory address. Table1.Array holds a reference to that memory address, which has the table. Table2.Array holds a reference to Table1.Array, which is pointing towards the memory address. Both these variables are now pointed at the same memory address and remain unrelated.

This is what it would look like visually:

-- As code:
local Table1 = {
    Name = "Table1",
    Array = {1, "A", true, print}
}

local Table2 = {
    Name = "Table2",
    Array = Table1.Array
}

-- As literal:
local Table1 = {
    Name = "Table1",
    Array = "address_0000020713DFDA60"
}

local Table2 = {
    Name = "Table2",
    Array = "address_0000020713DFDA60"
}

You can indeed edit Table1 with Table2 considering they point to the same address, but Table1.Array and Table2.Array are still different variables. Once you assign a different variable, they don’t point to the same thing. Table1.Array continues pointing to the table and Table2.Array points to something else.


Does this all help you understand better?

7 Likes

Yes, that really clear things up a lot.

Thank you! :+1:

is there a deepcopy method in Luau just-in-case?

no, table.copy is shallow but you can easily find or write your own deep copy function

i need some kind of reflection in order to implement it, right?