TL;DR
Why does std.mem.sort take in a context structure? I’ve only used Rust, Julia, Python and OCaml and their sort implementations don’t do that. This seems like (to me, who’s not a very good programmer) a strange thing a standard library sort function to have? The other languages I know don’t have that, but I did learn that libc has it, which makes me think there’s a common use case for this that I’m unaware of. Could somebody point me in the right direction to learn why this is such a common need? Tysm!
Context
I was looking at std.mem.sort, and I noticed it’s signature is this:
pub fn sort( comptime T: type, items: []T, context: anytype, comptime lessThanFn: fn (@TypeOf(context), lhs: T, rhs: T) bool, ) void
I couldn’t understand why it had the context field present because I’ve not seen that before in a standard library sort function. I was advised to look at the tests and I found tests in std.sort line 371,
test "sort with context in the middle of a slice" {
const Context = struct {
items: []i32,
pub fn lessThan(ctx: @This(), a: usize, b: usize) bool {
return ctx.items[a] < ctx.items[b];
}
pub fn swap(ctx: @This(), a: usize, b: usize) void {
return mem.swap(i32, &ctx.items[a], &ctx.items[b]);
}
};
const i32cases = [_][]const []const i32{
&[_][]const i32{
&[_]i32{ 0, 1, 8, 3, 6, 5, 4, 2, 9, 7, 10, 55, 32, 39, 58, 21, 88, 43, 22, 59 },
&[_]i32{ 50, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 22, 32, 39, 43, 55, 58, 59, 88 },
},
};
const ranges = [_]struct { start: usize, end: usize }{
.{ .start = 10, .end = 20 },
.{ .start = 1, .end = 11 },
.{ .start = 3, .end = 7 },
};
inline for (context_sort_funcs) |sortFn| {
for (i32cases) |case| {
for (ranges) |range| {
var buf: [20]i32 = undefined;
const slice = buf[0..case[0].len];
@memcpy(slice, case[0]);
sortFn(range.start, range.end, Context{ .items = slice });
try testing.expectEqualSlices(i32, case[1][range.start..range.end], slice[range.start..range.end]);
}
}
}
}
In this specific test, the context is a type erased structure that contains data to be sorted and conforms to the interface of having a fn swap(@This(), usize, usize) void function and a fn lessThan(@This(), usize, usize) bool function, and the algorithms context_sort_funcs are functions that take in bounds and the context structure and use the interface to sort the data.
My specific question is that is this kind of pattern very common in lower level programming languages? I’ve only used a few higher level languages (Julia, Python, OCaml and Rust) so I’m completely lost as to what the common use case for this is, and why couldn’t the end user of the sort function handle this, and the sort function itself just sorts arrays of things you can compare?