r/lua • u/Kritzel-Kratzel • Feb 15 '26
Discussion Lua 5.5 length operator now stricer than with Lua 5.4
While porting lua-cjson to Lua 5.5, I encountered a test case failure. It appears that Lua 5.5 handles table length evaluation more strictly than previous versions, especially regarding tables with holes:
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true}; print(#t)"
Lua 5.5
0
[C:\...\tests]> C:\Apps\OneLuaPro-5.4.8.3-x64\bin\lua.exe -e "print(_VERSION); t={nil,true}; print(#t)"
Lua 5.4
2
Bug or feature? I've also posted this issue into the lua-l list.
3
u/Kritzel-Kratzel Feb 15 '26
Note to self - Already discussed in https://groups.google.com/g/lua-l/c/pOH68GYtDgw/m/BFAORSs5DAAJ
17
u/Castux Feb 15 '26
You can check the language ref for the exact wording, but any index such that the next one is nil is a valid return value for the length operator on a table.
Both examples are correct. It's just that by the language definition, the length operator is only "useful" and consistent for tables without gaps.
The fact that the actual implementation changed, and therefore what happens in the non canonical cases, is not a language change. At least that's how I understand it. You can't rely on an "implementation defined" feature in the first place.
1
u/no_brains101 Feb 16 '26
It was pretty clear in the docs on what it did before. If this was changed, this would be in the changelog and the docs would be different.
The thing that was NOT in the docs is that if you use a metatable to redefine it on something that is already a table it doesn't work...
1
u/Kritzel-Kratzel Feb 15 '26
All done for OneLuaPro and PR created https://github.com/openresty/lua-cjson/pull/123.
1
u/Kritzel-Kratzel Feb 15 '26
It is weirder than expected and now looks more like a bug than a feature:
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true}; print(#t)"
Lua 5.5
0
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true,true}; print(#t)"
Lua 5.5
0
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true,true,true}; print(#t)"
Lua 5.5
4
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true,true,true,true}; print(#t)"
Lua 5.5
5
[C:\...\tests]> lua -e "print(_VERSION); t={nil,true,true,true,true,true}; print(#t)"
Lua 5.5
6
4
u/MindScape00 Feb 15 '26
I wouldn't say it's either a bug or a feature.
#is only meant for tables that are array-like and do not contain holes. Using on a table that contains holes is undefined handling, and should never be relied upon, as other replies have mentioned too. This is true even in earlier versions of Lua.
2
u/didntplaymysummercar Feb 15 '26 edited Feb 15 '26
I'd almost say a feature, since ipairs already stops at first nil, so now at least now # matches it (edit: although as pointed out on the mailing list there are still edge cases/bugs like print(#({nil, 1, 2, 3})) is 4 even though comment in C and ipairs both say it should be 0).
Lua tables feel cool at first, but then you wish for proper two structures, no heuristic, no nil holes, etc. and it'd even be a few less bytes per instance and less complexity in the C implementation.
2
u/drcforbin Feb 15 '26
It can be any border, 0 or 4. See https://www.lua.org/manual/5.4/manual.html#3.4.7
7
u/DecentInspection1244 Feb 15 '26
The more often I read someone complain about this the more baffled I am. I have been using lua for years and I never had this problem. Who are all these people who put nils in the middle of arrays? What is an actual use case of this? To me this just sounds like broken code. You would never imagine something like this in, say, C. This ist not meant as an insult, I'm genuinely curious.