Coercions and compile time known numbers


The docs say:

when a number is comptime-known to be representable in
the destination type, it may be coerced.

What follows is an example
where u64 is initialized with 255, and then coerced to u8 simply by assignment.

So, I tried something similar, only between signed and unsigned:

const std = @import(“std”);
const expect = std.testing.expect;

test “implicit unsigned integer to signed integer” {
var a: u8 = 2;
var b: i8 = a;
try expect(b == 2);

and the result was:

signed 8-bit int cannot represent all possible unsigned 8-bit values

Okay, I know that :slight_smile: But I don’t have all possible unsigned 8-bit values, what I have takes
only 2 bits to represent.

So, a question:

What exactly does may (as in may be coerced) mean?

The way I see it, the problem with docs is that everything is written by somebody (and for somebody:)
who already knows how stuff works, and then can use it as a reminder, or a reference.

But learning anything from the docs in the current form is anything but easy. If Zig is announced as a simple language, I think it definitely needs clear documentation.

My take is that there are no simple languages. Many are too complicated, and some are trying
to be better. But simple? No, you can’t have anything simple when you are trying to make
something usable by humans, and machines still work as they always have.

But without clear, unambiguous docs, and with clear definitions for all important concepts, even relatively simple can turn out to be much harder than it needs to be.

I would try two alternatives:

test “const u8” {
  const a: u8 = 2;
  var b: i8 = a;
  try expect(b == 2);

test “var u2” {
  var a: u2 = 2;
  var b: i8 = a;
  try expect(b == 2);

Once the language stabilizes, the docs should become much more thorough.

1 Like

You have to use explicit casts since you’re using var instead of const for a.

Here’s your case:

const std = @import("std");
const expect = std.testing.expect;

test "explicit unsigned integer to signed integer" {
    var a: u8 = 2;
    var b: i8 = @intCast(a);
    try expect(b == 2);
1 Like

Appreciate the answers.

So, I guess it would be fair to say that for coercing to work in such cases when new type cannot hold
all possible values of the old type (even if old type is initialized with value
that is in range of the new type), old type has to be declared const.

But, if you have to use variables, you cannot rely on coercion, and you need to cast explicitly.


While there might also be other stuff in play in this example, the basic concept that explains the behavior is that for Zig vars are not considered comptime known, while consts are (when hardcoded to a literal or the value is otherwise comptime known).

In general I think the idea is that Zig is being conservative, a var could have its pointer taken and be modified from somewhere else, while consts cannot be modified even if you take their pointer.

So in var foo: u8 = 2; the value of foo is comptime-known, but foo itself could still be modified at runtime.