Memory Leak with my generic grid implementation

Hey everyone, I am having an issue with understanding a memory leak I keep having with my implementation of a grid. I was trying to implement a “clone” method but it keeps having memory issues and I don’t understand how to fix it. Maybe someone here can help me out with understanding what I’m doing wrong.

const std = @import("std");

pub fn Grid(comptime T: type) type {
    return struct {
        const Self = @This();

        allocator: std.mem.Allocator,
        cols: usize,
        rows: usize,
        cells: []T,

        pub fn init(allocator: std.mem.Allocator, cols: usize, rows: usize) !Self {
            const cells = try allocator.alloc(T, cols * rows);
            return .{ .allocator = allocator, .cols = cols, .rows = rows, .cells = cells };
        }

        pub fn deinit(self: *Self) void {
            self.allocator.free(self.cells);
        }

        pub fn fromSlice(allocator: std.mem.Allocator, cols: usize, rows: usize, slice: []T) !Self {
            std.debug.assert(slice.len == cols * rows);
            const cells = try allocator.dupe(T, slice);
            return .{ .allocator = allocator, .cols = cols, .rows = rows, .cells = cells };
        }

        pub fn clone(self: Self) !Self {
            return try Self.fromSlice(self.allocator, self.cols, self.rows, self.cells);
        }

        pub fn inBounds(self: Self, col: usize, row: usize) bool {
            return !(col >= self.cols or row >= self.rows);
        }

        pub fn getIdx(self: Self, col: usize, row: usize) usize {
            std.debug.assert(row < self.rows);
            std.debug.assert(col < self.cols);
            return if (self.rows < self.cols) self.rows * row + col else self.cols * col + row;
        }

        pub fn get(self: Self, col: usize, row: usize) T {
            return self.cells[self.getIdx(col, row)];
        }

        pub fn getPtr(self: Self, col: usize, row: usize) *T {
            return &self.cells[self.getIdx(col, row)];
        }

        pub fn set(self: *Self, col: usize, row: usize, val: T) usize {
            const idx = self.getIdx(col, row);
            self.cells[idx] = val;
            return idx;
        }
    };
}

// init test
test "grid init test" {
    var g = try Grid(u8).init(std.testing.allocator, 2, 2);
    defer g.deinit();
}

// from slice test
test "grid from slice test" {
    var array = [_]u8{ 0, 1, 0, 1 };
    var g = try Grid(u8).fromSlice(std.testing.allocator, 2, 2, &array);
    defer g.deinit();
}

// grid clone test
test "grid clone test" {
    var grid_list = std.ArrayList(Grid(u8)).init(std.testing.allocator);
    defer {
        for (grid_list.items) |*item| item.deinit();
        grid_list.deinit();
    }

    var array = [_]u8{ 0, 1, 0, 1 };
    var g = try Grid(u8).fromSlice(std.testing.allocator, 2, 2, &array);
    defer g.deinit();

    for (9..100) |_| {
        const gc = try g.clone();
        try grid_list.append(gc);
    }
}

I think you forgot to deinit the Grid (var g = try Grid(u8)...) that you copy from in your last test.

I think it would generally be advisable to split your test into multiple tests, as it helps to narrow down the exact place, where the issue occurs :slight_smile:

That actually fixed it! Thanks! Kinda dumb from my side.