Specifying metatables on new objects in Lua

372 views Asked by At

Method/1

Dog = {}
function Dog:new()
  local newObj = {sound = 'woof'}
  return setmetatable(newObj,   { __index = self })
end

Method/2

Dog = {}
function Dog:new()
  local newObj = {sound = 'woof'}
  self.__index = self
  return setmetatable(newObj, self)
end

Most of the times I have seen people using the self.__index = self method, which to me seems clumsy. Why pass the whole Dog object with all the additional methods which doesn't constitute a metatable to setmetatable? Method/1 is good for setting the new objects's metatable.__index to the Dog object, it is also cleaner.

Is there a good reason to use Method/2 instead of Method/1?


Some additional code to provide context, it works with both methods

function Dog:makeSound()
  print('I say ' .. self.sound)
end

mrDog = Dog:new()
mrDog:makeSound()
3

There are 3 answers

2
finnw On

If you want an __eq metamethod, you must have just one metatable shared between all instances or it will not work. Your method #1 will not work in this case.

But the metatable does not need to be Dog, it can be a dedicated metatable:

Method 3.

Dog = {}
local DogMeta = {__index = Dog}
function Dog:new(name)
  local newObj = {sound = 'woof', name = name}
  return setmetatable(newObj, DogMeta)
end
function DogMeta.__eq(dog1, dog2)
  return dog1.name == dog2.name
end
0
Yu Hao On

Method/2 is a little more optimized than Method/1 because it doesn't need to create an extra table as its metatable. It uses itself as the metatable.

Since you said that you think Method/1 is cleaner in your question, feel free to use it. I don't think the performance difference between the two would matter in most cases. Readability is almost always more important.

0
greatwolf On

While both approaches achieve the same end behavior, someone might prefer method 2 because it better conforms with the "recycle resource over creation" policy. Method 2 will always use one table Dog as the metatable, regardless of how many Dog Objects you create. Method 1, OTOH, will create a new anonymous table just to act as a meta for every Dog object created.

However, method 1 is probably easier to read and reason about for newcomers to the language since it doesn't mix the concern of metatables and object definition together.