ziglua icon indicating copy to clipboard operation
ziglua copied to clipboard

Optional Fields in toStruct do not get null value

Open tmrlvi opened this issue 5 months ago • 2 comments

Summary

When a struct contains an optional field without a default value, an arbitrary value is assign (I suspect it is the uninitialized memory).

Possible solution

I noticed that if I change https://github.com/natecraddock/ziglua/blob/6889b2d90ee6ae96810a9f04ec7c62d9aa91d088/src/lib.zig#L4856-L4862

to

 if (lua_field_type == .nil) { 
     if (field.defaultValue()) |default| { 
         @field(result, field.name) = default; 
     } else if (field_type_info != .optional) { 
         return error.LuaTableMissingValue; 
     } else {
         @field(result, field.name) = null;
      }
 } else { 

resolves the issue, but I'm not sure this is the right approach (hence issue and not PR)

Reproducing

Code

const zlua = @import("zlua");
const std = @import("std");

const Lua = zlua.Lua;

const A = struct {
    a: ?u64,
};


pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    var lua = try Lua.init(allocator);
    defer lua.deinit();

    try lua.doString("return {a=4};");
    std.debug.print("{}\n", .{try lua.toStruct(A, null, false, 1)});
    lua.pop(1);

    try lua.doString("return {a=nil};");
    std.debug.print("{}\n", .{try lua.toStruct(A, null, false, 1)});
    lua.pop(1);
}

Expected

main.A{ .a = 4 }
main.A{ .a = null }

Result

main.A{ .a = 4 }
main.A{ .a = 12297829382473034410 }

Workaround

Defining a default value of null circumvent this issue. In the example above:

const A = struct {
    a: ?u64 = null,
};

tmrlvi avatar Aug 10 '25 08:08 tmrlvi

I'd accept a PR with the Possible Solution fix. I'm not sure the API as-written is in a state that I'm completely happy with, given that nullability and gracefully handling type mismatches are fundamental differences between Zig and Lua, but it seems clear that null is intended in this situation and should not require a default value.

robbielyman avatar Aug 11 '25 20:08 robbielyman

That's a good point - on the lua side all the following are equivalent:

A = {a=undefined_variable};
B = {a=nil};
C =  {};

So actually defaulting optionals to null might be a footgun that will cover issues in the lua code. However, this is an issue even when default value exists, and I think people usually expect implicit null when using optionals.

I come to think the solution above is the best option. I'll probably submit the PR later this week

tmrlvi avatar Aug 17 '25 07:08 tmrlvi