What Zig felt like, coming from Rust
fn runQuery(json: *Value, qstr: []const u8, a: Allocator) !q.JsonPathResult { var iter = q.JsonPathIter.init(json, a); errdefer iter.deinit(); try q.query(qstr, &iter); return iter.toResult(parsed); // ownership moves to caller } fn cacheAndLog(json: *Value, qstr: []const u8, a: Allocator, cache: *std.ArrayList(q.JsonPathResult)) !void { var result = try runQuery(json, qstr, a); try cache.append(result); // cache now holds a (shallow) copy of result's pointers defer result.deinit(); // BUG: frees the same heap data cache.items still points to printResults(&result); } fn processAll(json: *Value, queries: [][]const u8, a: Allocator) !void { var cache = std.ArrayList(q.JsonPathResult).init(a); defer { for (cache.items) |*r| r.deinit(); // frees the SAME memory Layer 2 already freed cache.deinit(); } for (queries) |qs| try cacheAndLog(json, qs, a, &cache); }