Skip to content

Unpacker.readArray is uninstantiable; README custom-format example does not compile #21

Description

@acoustid-bot

Unpacker.readArray cannot be instantiated with any type argument, so the "completely custom format" example in the README does not compile.

Cause

readArray passes its type parameter straight through to unpackArray, but the two disagree about what that parameter means:

// src/msgpack.zig:208
pub fn readArray(self: Unpacker, comptime T: type) ![]T {
    return unpackArray(self.reader, self.allocator, T);
}

unpackArray expects T to be the slice type — it derives the element type itself via std.meta.Child(NonOptional(T)) and returns T (src/array.zig:71). readArray instead declares ![]T, i.e. it treats T as the element type. Both readings fail:

  • readArray(u32)unpackArray computes std.meta.Child(u32)"Expected pointer, optional, array or vector type, found 'u32'"
  • readArray([]u32)unpackArray returns []u32, but the signature promises [][]u32"pointer type child 'u32' cannot cast into pointer type child '[]u32'"

Note Packer.writeArray(comptime T, value: []const T) and Unpacker.readArrayInto(comptime T, buffer: []T) both take the element type, so element-type is the consistent convention for the Packer/Unpacker layer.

Reproducer

This is copied verbatim from the "Or you can use a completely custom format" section of README.md:

const Message = struct {
    items: []u32,

    pub fn msgpackWrite(self: Message, packer: anytype) !void {
        try packer.writeArray(u32, self.items);
    }

    pub fn msgpackRead(unpacker: anytype) !Message {
        const items = try unpacker.readArray(u32);
        return Message{ .items = items };
    }
};

test "README custom format example" {
    const data = [_]u8{ 0x93, 0x01, 0x02, 0x03 };
    var r = std.Io.Reader.fixed(&data);
    _ = try msgpack.decodeLeaky(Message, std.testing.allocator, &r);
}

It fails to compile against main (Zig 0.16.0).

Suggested fix

Keep the element-type convention and pass the slice type down:

pub fn readArray(self: Unpacker, comptime T: type) ![]T {
    return unpackArray(self.reader, self.allocator, []T);
}

I confirmed locally that this one-line change makes the README example above compile and pass, with the rest of the suite still green.

Related: Array(T) in src/array.zig:106

The same generic-never-instantiated situation hides two more errors in the Array(T) helper, which nothing in the repo constructs:

  • msgpackWrite calls packer.writeArray(self.data) with one argument; the method takes two (comptime T, value).
  • msgpackRead calls unpacker.readArray([]T) and assigns the result to a []T field.

Worth fixing (or deleting) alongside this. The neighbouring String and Binary helpers are fine.

Why CI is green

refAllDecls does not instantiate generic functions, so test { _ = std.testing.refAllDecls(@This()); } in src/msgpack.zig never type-checks either of these. A small smoke test that actually calls every Packer/Unpacker method once would have caught it — I wrote one while investigating and it found exactly these plus #22.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions