Skip to content

Optional Fields in toStruct do not get null value #171

Closed

Description

@tmrlvi

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

ziglua/src/lib.zig

Lines 4856 to 4862 in 6889b2d

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

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,
};

Activity

  1. changed the title [-]Optional Fields in toStruct[/-] [+]Optional Fields in toStruct do not get null value[/+] on Aug 10, 2025
  2. robbielyman commented on Aug 11, 2025

    @robbielyman
    Collaborator

    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.

  3. tmrlvi commented on Aug 17, 2025

    @tmrlvi
    Author

    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

  4. self-assigned this
    on Apr 15, 2026
  5. added this to the 0.7.0 milestone on Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions