2

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()
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
George
  • 15,241
  • 22
  • 66
  • 83

3 Answers3

1

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.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
1

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.

greatwolf
  • 20,287
  • 13
  • 71
  • 105
1

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
Community
  • 1
  • 1
finnw
  • 47,861
  • 24
  • 143
  • 221
  • 3
    `Method/2` work correctly if you just define `function Dog:__eq(rhs)`. This method allows you define meta and non meta methods in same way. Because of this i prefer this method. – moteus Dec 13 '13 at 08:26
  • @moteus you are right, but I think what finnw is trying to say is that the usual approach to compare for 'type' equality is to check that object's metatable. This approach won't work if every object has a different mt. His example doesn't quite emphasize that unfortunately. – greatwolf Dec 13 '13 at 23:04