Need help with formatting custom type

Im attempting to create a generic Linked List implementation in zig, and I wanted to make a custom format function for it. This is my code:

const std = @import(“std”);

fn Node(comptime T: type) type {
return struct {
value: T,
next: ?\*Node(T),

    const Self = @This();

    pub fn init(value: T) Self {
        return .{ .next = null, .value = value };
    }
};

}

fn List(comptime T: type) type {
return struct {
head: ?\*Node(T),
len: usize,
allocator: std.mem.Allocator,

    const Self = @This();

    pub fn init(allocator: std.mem.Allocator) Self {
        return .{ .allocator = allocator, .head = null, .len = 0 };
    }

    pub fn format(self: Self, writer: anytype) !void {
        if (self.head == null) {
            try writer.print("[]", .{});
            return;
        }

        try writer.print("[", .{});

        var current = self.head.?;
        while (current.next) |next_node| {
            try writer.print("{}, ", .{current.value});
            current = next_node;
        } else {
            try writer.print("{}", .{current.value});
        }
    }

    pub fn push_back(self: *Self, value: T) !void {
        const new_node = try self.allocator.create(Node(T));
        new_node.* = Node(T).init(value);

        if (self.head == null) {
            self.head = new_node;
            self.len += 1;
            return;
        }

        var current = self.head.?;
        while (current.next) |next_node| {
            current = next_node;
        } else {
            current.next = new_node;
            self.len += 1;
        }
    }

    pub fn deinit(self: *Self) void {
        if (self.head == null) return;

        var current = self.head.?;
        while (current.next) |next_node| {
            self.allocator.destroy(current);

            current = next_node;
        } else {
            self.allocator.destroy(current);
        }

        self.len = 0;
    }
};

}

pub fn main() void {}

test “testing list init, deinit and push_back” {
const ally = std.testing.allocator;

var list = List(i32).init(ally);
defer list.deinit();

try list.push_back(20);
try list.push_back(30);
try list.push_back(67);

}

test “list format specifier” {
const ally = std.testing.allocator;

var buffer: [64]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);

var list = List(i32).init(ally);
defer list.deinit();

try list.push_back(10);
try list.push_back(20);
try list.push_back(30);

try stdout_writer.interface.print("list: {f}\n", .{list});
try stdout_writer.interface.flush();

try std.testing.expect(true);

}

when i run zig build test however, it gets stuck on running the format specifier test and never stops running.

I’m completely stuck. I need some help in seeing where i went wrong.

Have you tried stepping through your code in a debugger to see where it is looping? Some instructions in this thread: Zig Debugging with LLDB

format calls print that uses format.

Implement format without using print. print calls format and not the other way.

I understand that but I don’t understand why its looping, since Im not calling the List.format() function in the format function. Im only calling the format() function for the type T.

I’m stuck again. I can’t figure this out. It’s confusing as I’ve seen many implementations for format() that use print().

This is so odd… I slightly altered the code and now it works?

pub fn format(self: Self, writer: *std.Io.Writer) !void {
     if (self.len == 0) {
         try writer.writeAll(“[]”);
         return;
     }

        try writer.writeByte('[');

        var current = self.head.?;
        while (current.next) |next| {
            try writer.print("{}, ", .{current.value});
            current = next;
        } else {
            try writer.print("{}]", .{current.value});
        }
    }

are you using the custom backend? on linux x86_64 it is enabled in debug builds by default.

If so does the issue persist if you force llvm with .use_llvm = true in exe/lib/obj/test options?

1 Like

Your original code works for me on Linux x86_64 with both the LLVM and Debug backends on master.