Trouble with index & newindex

I’m not really sure why this isn’t working properly as I already read Metatables, All about Object Oriented Programming, All you need to know about Metatables and Metamethods

I’m not new to using Metatables, however I haven’t used Metamethods like this before, thus I require assistance with getting this code to work as expected and to understand index & newindex

local Table = setmetatable({},{
	__index = function(self,index)
		print("__index",self,index)
		return rawget(self,index)
	end,
	
	__newindex = function(self,index,value)
		print("__newindex",self,index,value)
		rawset(self,index,value)
	end,
})

Table.A = 1
Table.A = 2
if Table.A then
	print(Table.A)
end

--[[ Output

	__newindex table: 0xfde0cf082ad17e64 A 1
	2

--]]

--[[ Expected Output

	__newindex table: 0xfde0cf082ad17e64 A 1
	__newindex table: 0xfde0cf082ad17e64 A 2
	__index table: 0xfde0cf082ad17e64 A
	__index table: 0xfde0cf082ad17e64 A
	2

--]]

There wasn’t a lot of materials on index & newindex to begin with and I didn’t find a similar Thread, so if you do please let me know.

2 Likes

First of all thank you for reading my article hehe!

Remember that after this line

Table.A =  1

You’re setting the key A inside of Table (with rawset inside of __newindex), meaning the next time you do Table.A = 2 you’re not gonna fire __newindex because now A exists. Also __index won’t fire because A exists as well so you’re not indexing something that is nil.

3 Likes

I see, thank you!

To clarify what I am looking to achieve is to detect changes to Values of Indexes inside the Table,

From what I understand:

  • newindex will trigger anytime a Value is assign to an Index inside a Table
  • index will trigger only if an Index exist inside a Table
1 Like

Ah I see what you’re doing! Well this concept is actually a thing used very often in lua, it’s called a proxy table, the PIL does a great job explaining it. Here is a link. If you find yourself not understanding something about it I’m happy to answer

6 Likes

Although it’s already answered, I’ll share an implementation using Bindables (yeah networking and such) for a table.changed.

To get __newindex to trigger for existing values you’ll need a proxy table as starmaq already said.

Alternatively you could create an identical table and set __newindex to check whether a certain index has the same value as the one being set to the index and then fire the bindable passing the index and value (if what you want is the __newindex firing for existing values apparently).

  local data = {} 
 
  function data:__call(t) 
  assert(type(t) == "table", ("bad arg #1 table expected, got %s"):format(type(t)))  

  local t2 = t

  local bindable = Instance.new("BindableEvent")
  t = setmetatable({}, { 
      __index = t,
      __newindex = function(self, index, value)
      if t2[index] ~= value then
      bindable:Fire(index, value) 
      rawset(t2, index, value) -- C stack overflow no more
      end
  end
  })

   t.Changed = bindable.Event  
     return t
  end

  return setmetatable(data, data)
-------
  local data = require(module)({coins = 1, cash = 5})
  data.Changed:Connect(print)
  data.coins = 1 -- nothing
  data.coins = 10 -- prints 'coins 10'
4 Likes

Is there any way to make the maintable iteratable?

1 Like