On type choices and "idiomatic" way to add a negative number to usize

So here is the resulting function applyShift2 and some tests:

const std = @import("std");

// make it a function to make it easier to discuss alternative functions
fn applyShift(orig: usize, shift: isize) usize {
    const s: isize = shift;
    var u: usize = orig;
    if (s < 0)
        u -|= @intCast(-s)
    else
        u +|= @intCast(s);
    return u;
}

fn applyShift2(orig: usize, shift: isize) usize {
    const s: isize = shift;
    var u: usize = orig;
    if (s < 0)
        u -|= @abs(s)
    else
        u +|= @intCast(s);
    return u;
}

// like 2 but without the var
pub fn applyShift3(u: usize, s: isize) usize {
    return if (s < 0) u -| @abs(s) else u +| @as(usize, @intCast(s));
}

const shiftFn = *const fn (usize, isize) usize;

test "overflow and underflow" {
    const t = std.testing;
    const utils = struct {
        fn testIt(f: shiftFn) !void {
            try t.expectEqual(@as(usize, 0), f(0, 0));
            try t.expectEqual(@as(usize, @intCast(std.math.maxInt(isize))), f(0, std.math.maxInt(isize)));
            try t.expectEqual(@as(usize, 0), f(0, std.math.minInt(isize)));
            try t.expectEqual(@as(usize, std.math.maxInt(usize)), f(std.math.maxInt(usize), 0));
            try t.expectEqual(@as(usize, std.math.maxInt(usize)), f(std.math.maxInt(usize), std.math.maxInt(isize)));
        }
    };
    // try utils.testIt(applyShift); // integer overflow
    try utils.testIt(applyShift2);
    try utils.testIt(applyShift3);
}

I wonder if there is some other test that should be added, I currently can’t come up with more.
The applyShift3 is like 2 but using if expression to avoid the var and I quite like how it becomes a fairly readable oneliner.

4 Likes