To the best of my understanding, both subtracted and subtracted_but_via_a_function are calculated the same way: Two u32 values are coerced to i32 (which, yes, I know that isn’t safe…but bear with me…), then are subtracted. Yet, these behave in different ways:
If I comment out the bottom two lines, subtracted is calculated just fine, and printed.
If the bottom two lines are kept in, I get the following error message
scratch.zig:4:21: error: expected type 'i32', found 'u32'
return @as(i32, a) - @as(i32, b);
^
which seems to imply that Zig expected an i32 value to be passed to @as(i32, _). Isn’t that the one type you wouldn’t expect to be passed to a coercion-to-i32? And why is casting-a-u32-to-an-i32 permitted when not part of a separate function?
That error message is a little misleading. The @as builtin performs type coercion, it can only convert between types when the conversion is unambiguous and safe. So
var a: u32 = 50;
_ = &a;
var b: i32 = a;
_ = &b;
is equivalent to
var a: u32 = 50;
_ = &a;
var b = @as(i32, a);
_ = &b;
In both cases there is an error since not all u32 values fit in i32.
If the value is evaluated at comptime, such as when assigning a literal to a constant, then the compiler can perform the coercion if the value can safely coerce. In your case 50 does fit in an i32.
const a: u32 = 50;
var b i32 = a; // no error, 50 fits in i32
_ = &b;
const a: u32 = 0xffffffff;
var b i32 = a; // error, 0xffffffff doesn't fit in i32
_ = &b;
Function calls are only evaluated at comptime if they are specifically marked with comptime (or if the function has a comptime return type). So when you moved the type coercion into a function call it resulted in errors.
If you want to cast between runtime integer values you should use the @intCast builtin.
var a: u32 = 50;
_ = &a;
var b: i32 = @intCast(a);
_ = &b;
Note that such a cast assumes the value will fit and is safety checked in safe builds.
var a: u32 = 0xffffffff;
_ = &a;
var b: i32 = @intCast(a); // runtime error in safe build
_ = &b;
Ah, I see - that’s helpful, thanks! I’ve seen the concept of comptime thrown around in docs, but I haven’t really grappled with it - this description makes sense, though. Thank you!
@as(T,v) exists to provide a type where zig cannot infer a type, or it defaults to a type that you dont want.
for example, fn foo(anytype)... does stuff
but foo(5) passes a comptime_int but thats not the behaviour you want you can do foo(@as(i32, 5)) to force zig to use an i32 instead of comptime_int
a more likely example, your doing some math that involves multiple casts