Testing return value of a mutating function in Zig

Consider the following reverseArray function. It mutates the array in-place, and returns the result. Of course, that’s a rather redundant thing to do. But it represents a general class of functions that has side effects and a return value.

fn reverseArray(arr: []u32) []u32 { ... }

Now, we want to test it. The following code doesn’t work, because the inline slice &[_]u32{ 1, 2, 3 } is read-only.

test "reverseArray: works" {
  try std.testing.expectEqualSlices(
    u32,
    &[_]u32{ 3, 2, 1 },
    reverseArray(&[_]u32{ 1, 2, 3 }), // Fails
  );
}

We need to make the input slice mutable. The next one works. This is the “stack approach”.

test "reverseArray: works" {
  var mock_arr = [_]u32{ 1, 2, 3 };
  try std.testing.expectEqualSlices(
    u32,
    &[_]u32{ 3, 2, 1 },
    reverseArray(&mock_arr),
  );
}

However, var mock_arr = [_]u32{ 1, 2, 3 } makes stack allocation. And that might not be favorable in some cases.

So, here comes the “heap approach” with the testing allocator.

test "reverseArray: works" {
  const mock_arr = try std.testing.allocator.dupe(u32, &[_]u32{ 1, 2, 3 });
  defer std.testing.allocator.free(mock_arr);

  try std.testing.expectEqualSlices(
    u32,
    &[_]u32{ 3, 2, 1 },
    reverseArray(mock_arr),
  );
}

In any case, having a function do both side effects and return something is not a good idea. It’s better if a function does exactly one thing.