So I started working on a module to generate Ids and came across a pretty infuriating problem. I've simplified the following code so it doesn't achieve its intended functionality, but the same problem still occurs for demonstration purposes.

The Code:


local IdGen = {}   

local idGenMeta = {}

idGenMeta.__index = IdGen

function IdGen:New()

    local gen = {}

    gen.ids = {}

    return setmetatable(gen, idGenMeta)

end

function IdGen:MakeId() --This is servicable but maybe a rewrite is in order

    local ids = self.ids
    ids[#ids + 1] = #ids + 1

end

function IdGen:RemoveId(id)

    print("Current Id:", id)

    local ids = self.ids

    print("Id Table Contents:", unpack(ids))
    print("Id Table Length:", #ids)

    ids[id] = nil
    print("Index", id, "of Id table set to nil")

    print("Id Table Contents:", unpack(ids))
    print("Id Table Length:", #ids, "\n")

end

----Operations

    local MyGen = IdGen:New()

    for index = 1, 5 do
        MyGen:MakeId()
    end
 
    MyGen:RemoveId(3)
    MyGen:RemoveId(4)

--[[

	OUTPUT:

	  Current Id: 3
	  Id Table Contents: 1 2 3 4 5
	  Id Table Length: 5
	  Index 3 of Id table set to nil
	  Id Table Contents: 1 2 nil 4 5
	  Id Table Length: 5 
	  
	  Current Id: 4
	  Id Table Contents: 1 2 nil 4 5
	  Id Table Length: 5
	  Index 4 of Id table set to nil
	  Id Table Contents: 1 2
	  Id Table Length: 2 

]]

The problem occurs at line 50 and is visible in the second block of text in the output, where the fourth index is being removed from the module. The 5th value in the table seems to disappear out of nowhere, as though it had been set to nil when that doesn't appear to happen anywhere in the code.

I tried to reproduce this bug in a separate script and this was the result:


    local ids = {1, 2, 3, 4, 5}

    print("Id Table Contents:", unpack(ids))
    print("Id Table Length:", #ids, "\n")
    
    ids[3] = nil

    print("Index 3 of Id table set to nil ")

    print("Id Table Contents:", unpack(ids))
    print("Id Table Length:", #ids, "\n")

    ids[4] = nil

    print("Index 3 of Id table set to nil")

    print("Id Table Contents:", unpack(ids))
    print("Id Table Length:", #ids)

--[[

	OUTPUT:

	  Id Table Contents: 1 2 3 4 5
	  Id Table Length: 5 
	  
	  Index 3 of Id table set to nil 
	  Id Table Contents: 1 2 nil 4 5
	  Id Table Length: 5 
	  
	  Index 3 of Id table set to nil
	  Id Table Contents: 1 2 nil nil 5
	  Id Table Length: 5

]]

As you can see, there's a completely different result. The 5th value remains in the table as it's supposed to. As far as I can tell, the same operations took place, so I'm at a loss as to why this is happening.


EDIT: Through further testing, I found another inexplicable issue. If I add the code print("Id Table Contents:", unpack(MyGen.ids)) to line 48 in the first script, I get the following output:

Id Table Contents: 1

It's as though the other four values have disappeared, except if I check each element all of the values still exist and #id still results in a value of 5. This appears to be a completely separate problem compared to the first, since in in the first problem the fifth element actually disappears from the table and #id results in a value of 2.

If either of these problems are explainable, I would be extremely appreciative. I'm starting to think my studio is bugged or something.

3 comments

1

I think your issue here is the lack of table.remove and table.insert use. Explicitly setting an index to nil in an array doesn't implicitly shift preceding elements down one to the right--only table.remove does this. Also, constructors should never use : notation, only . notation. The behavior of the length operator # is undefined on sparse arrays, and unpack stops when there is a nil va

0

ty for the info on the length operator interaction with sparse tables, it looks like that plus however unpack works was causing this issue for me bc the 5th element still exists, it's just not affecting the length operator or the unpack output. If unpack were to stop on nil values, why would it print tables with nil values but it would only print the first element in a table with no nil values?

0

I have a theory that the fifth element is being moved to a hash table to conserve memory since there are too many nil values between the last two non-nil values. That would explain why the length operator returns 2 and unpack only returns the first two elements in my first problem. But I'm still unsure as to why unpack only prints the first element in the second problem I came across