I have some code where I have a Position which is just a struct { x: usize, y: usize }, and I want to obtain a new Position with new x and y based on a given direction enum value.
My initial attempt looked like this, which should show my intent:
const new_position: Position = .{
.x = position.x + switch (direction) {
.right => 1,
.left => -1,
else => 0,
},
.y = position.y + switch (direction) {
.down => 1,
.up => -1,
else => 0,
},
};
However, the first error I get from this is:
src/grid.zig:11:27: error: value with comptime-only type 'comptime_int' depends on runtime control flow
.x = position.x + switch (direction) {
^~~~~~
So okay, we’ll make it so the switch evaluates to an isize instead of a comptime_int (although it doesn’t feel like a very concise way to do it):
const new_position: Position = .{
.x = position.x + @as(isize, switch (direction) {
.right => 1,
.left => -1,
else => 0,
}),
.y = position.y + @as(isize, switch (direction) {
.down => 1,
.up => -1,
else => 0,
}),
};
But then we get:
src/grid.zig:11:25: error: incompatible types: 'usize' and 'isize'
.x = position.x + @as(isize, switch (direction) {
~~~~~~~~~~~^
Okay, so the problem now is adding an isize to a usize. I’ve read other posts on this subject, but I’m still not sure I understand the issue. I get that these types each represent a different range of values but… is the suggestion that the problem is over/underflow or something? But subtracting an isize from an isize can still underflow, so that can’t be right. So my understanding of the problem must be wrong
So okay, let’s cast the left-side to an isize too and then cast the results back to a usize:
const new_position: Position = .{
.x = @intCast(@as(isize, @intCast(position.x)) + @as(isize, switch (direction) {
.right => 1,
.left => -1,
else => 0,
})),
.y = @intCast(@as(isize, @intCast(position.y)) + @as(isize, switch (direction) {
.down => 1,
.up => -1,
else => 0,
})),
};
Great, now the compiler is happy! But I really just did the most basic thing in response to the reported errors, and I feel like there must be some better way to do it and that I’m missing something important.
Ultimately, I can rewrite it as this, which requires no casts, but I would prefer the other approach if there were a nice way to express it without too much clutter:
const new_position: Position = switch (direction) {
.up => .{
.x = position.x,
.y = position.y - 1,
},
.right => .{
.x = position.x + 1,
.y = position.y,
},
.down => .{
.x = position.x,
.y = position.y + 1,
},
.left => .{
.x = position.x - 1,
.y = position.y,
},
};
So my question has two parts:
- What’s the nicer way to implement this?
- What am I not understanding about why I’m not allowed to add an
isizeto ausize? Why is it okay to add a negativecomptime_intto anusize, but not okay to add anisize?
Thanks!