4

I'm trying to teach a friend of mine how to lua, basically, and he comes to me with this code, and it stumps my head:

a = {name = "aaa"}

function a:new()
  self.x = "sss"
  o = {}
  setmetatable(o, self)
  self.__index = self
  return o
end

function a:some()
  return "[ " .. self.name .. " ]"
end

b = a:new()
print(b:some())
print(b.x)

which prints

[ aaa ] sss

both shouldnt be possible, since they were never set to o inside a:new

after some debugging, i look into it, and here some interesting things happen:

a = {name = "aaa", x = "sss"}

function a:new()
  o = {}
  print(o.x, self.x)
   -- nil sss
  setmetatable(o, self)
  print(o.x, self.x, o, self, self.__index, o.__index)
   -- nil sss table: 0x1001280 table: 0x1001320 table: 0x1001320 nil
  self.__index = self
  print(o.x, self.x, o, self, self.__index, o.__index)
   -- sss sss table: 0x1001280 table: 0x1001320 table: 0x1001320 table: 0x1001320
  return o
end

Note how on the third print, it returns the .x value of self, but it was called from o, which has no "relationship" with self, how is this possible?

Shadowjonathan
  • 253
  • 2
  • 11

1 Answers1

1

You've set table a as __index field in metatable for all tables that will be created with a:new(). When some non-existent field will be checked in the b table, value will be searched in a too. That's why you can find fields x or name in a table b, even if you didn't assign it explicitly.

Vlad
  • 5,450
  • 1
  • 12
  • 19
  • how is setting `self` to `self.__index` setting it for all tables? the most i expect from that is setting a recursive `__index`, nothing more – Shadowjonathan Nov 02 '17 at 21:07
  • 1
    `b = a:new()` is equivalent to `b = a.new(a)`, where `a` will be passed as the first argument, which is `self`. So when you do `setmetatable(o, self)`, you're effectively doing `setmetatable(o, a)`. That's why `a`'s fields will be available to all objects created with `a:new()` – Vlad Nov 02 '17 at 21:26
  • keep in mind that in the statement up there, `return o` is applied, which is a new object, and even if it returned a modified `a`, there is still the case of that `print` reporting weird stuff – Shadowjonathan Nov 02 '17 at 21:41
  • Returned object `o` has metatable set, that metatable has `a` set as `__index` field. If later you will access some filed of that returned object, and that field doesn't exist - then `__index` kicks in, searching required field in `a` table. Note that in your latter example you will get "weird" outputs only after you've set `__index` field, and note before. Familiarize yourself with metatables and metamethods. That's chapter 2.4 of Lua's manuals. – Vlad Nov 03 '17 at 09:49