zig icon indicating copy to clipboard operation
zig copied to clipboard

Proposal: Add a swapRanges function to std

Open largenumberhere opened this issue 1 month ago • 2 comments

What

I am proposing to add a function swapRanges to std.mem. Here is an example implementation.

fn swapRanges(comptime T: type, left: []T, right: []T) !void {
    if (left.len != right.len) {
        return error{SliceLengthMismatch}.SliceLengthMismatch;
    }

    for (0..left.len) |i| {
        const tmp = left[i];
        left[i] = right[i];
        right[i] = tmp;
    }
}

Why

Swapping the contents of two slices is a common low-level operation that should be added to the zig standard library. Implementing this in user code encourages code duplication, and is tedious. Additionally, this abstraction lends itself to future potential optimizations in implementation such as SIMD usage, parallelization, memory tricks, specializations, etc. Furthermore it is simple, small and there are no assumptions required for consuming code.

Why it fits

In zig, slices are treated as a first-class types that are passed around freely, iterated on, assigned and copied. However, there is a missing builtin low-level operation for them. A function to swap the contents of two slices would be very useful and it fits the established precedent of the standard library. There are plenty of examples of functions in the zig standard library for data swapping and general manipulation of slices. You will notice that some are fallible and equally straightforward to implement in userspace. Here are a few in std.mem:

std.mem.copyBackwards(comptime T: type, dest: []T, source: []const T) void
std.mem.swap(comptime T: type, a: *T, b: *T) void
std.mem.byteSwapAllElements(comptime Elem: type, slice: []Elem) void
std.mem.byteSwapAllFields(comptime S: type, ptr: *S) void
std.mem.eql(comptime T: type, a: []const T, b: []const T) bool
std.mem.alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8
std.mem.concat(allocator: Allocator, comptime T: type, slices: []const []const T) Allocator.Error![]T

Edge cases

If any assurances are needed, like enforcing non-overlapping buffers, they may easily be encoded in the function signature or enforced in implementation.

largenumberhere avatar Nov 26 '25 14:11 largenumberhere

Generally agree cause the most optimal swapping (with simd and all of that) would likely be unfun to implement as a user, though I'd argue that the function shouldn't return an error on slice length mismatch, instead just asserting a.len == b.len. My reasoning for that is that none of the other (non-allocating) std.mem functions return an error union, and that that behaviour would match @memmove and @memcpy.

IOKG04 avatar Nov 26 '25 22:11 IOKG04

What advantage (if any) would this provide over @memcpy?

jeffective avatar Nov 26 '25 23:11 jeffective