If you had one wish for zig, what would it be?

I want const a: u32 = worker.group * 64 + worker.local_rank to work wether worker uses u8 or u32 internally.

1 Like
const Animal = type{ Dog, Cat }; // like error unions, but for types

But I’d have two more in reality.

I want infer. Like this:

fn function(generic_param: infer T) ReturnType {
    ...
}

Instead of this:

fn function(comptime T: type, generic_param: T) ResultType {
    ...
}

Or this:

fn function(generic_param: anytype) ResultType {
    const T = @TypeOf(generic_param);
    ...
}

It has the potential for some amount of type matching, with arything that’s a structural type:

fn function(generic_ptr: *infer T) ResultType {
    ...
}

And also using result type location for userland functions:

fn function(arg: Arg) infer T {
    switch (@typeInfo(T)) {
        ...
    }
    ...
}
13 Likes

I would wish for ways to add constraints to anytype or comptime T: type that are standardized and composable.

Current conventions mostly use comptime, but there still doesn’t seem to be a consensus for applying type constraints to generics.

Lots of languages have a notion of type classes, and it’s a pattern I happen to enjoy using where applicable. Whether this belongs for a language like zig is debated, and some argue it could exist via library code.

I don’t know what the solution is, but I do think there is complexity in getting type errors from comptime type checks that you have to go read and understand in context.

2 Likes

So… a union(enum)?

5 Likes

Yes but as with anytype you wouldn’t need to switch over the type (so basically a named and type-restricted anytype).

1 Like

I think they mean assigning an ID to every type akin to how every error.ErrorTypeis assigned a u64 value. However, this only works for error unions by keeping a global set defined strictly by name. Types have a name and a scope, so in order to keep a global scope you’d have to use a types fully qualified name. Maybe with the dot notation this could exist?

Isn’t what the compiler already does? I mean, all types are unique from the compiler’s perspective and the compiler knows what the full type name is, so they could be indexed as well, no?

1 Like

For casting to feel less painful. There has to be a way.

I don’t understand why we don’t have a generic @cast(comptime T: type, expression) built-in for . Is it really that much of a footgun?

Yes, it is a footgun. If Zig did that, then it would buy into the whole C model where the single cast operator is actually overloaded to do very different things based on what the input and output types are. That overloading is exactly what the different builtins in Zig are trying to avoid.

1 Like

Using value: Type instead of @as(T, value) would be another wish of mine. But replacing casting altogether with one builtin would be problematic imo. It does very different things for different types, it introduces differert illegal behavior.

1 Like

I forgot to specify I was talking about numeric conversion only. So casting between different sized floats and integers. I can understand why pointer, const and bitcasting are separate.

So yeah, an overloaded cast for anything that’s semantically a number would be my wish.

1 Like

macro system

the reason why there are multiple kinds of casts for anything is because they have different behaviour/semantics!!! in a few cases there is only one option, but what about the cases where there are multiple? how would you differentiate them if it was just a single @cast builtin.

Enforcing the tag of a union to be comptime known:

const MyTypeGroup = union(comptime enum) {
     foo: Foo,
     bar: Bar,
};


fn (a: MyTypeGroup) void {
    // this is resolved at compile time
    switch(a) {
        ...
    }
}
1 Like

The full C99 designated-init feature set for struct initialization, everything else I can live with :wink:

2 Likes

It’s definitely arguable though whether a single one-for-all cast is any worse than C++ style granular casts. E.g. a cast is forcing a different ‘view’ on a specific piece of data, and that is always a potential footgun (if it wasn’t, no cast would be needed).

IMHO when casts actually require some ‘runtime actions’ (e.g. converting a float into an int, like 1.4 => 1) this shouldn’t be called a cast but a ‘conversion’. E.g. ‘cast’ should be reserved for assigning a different ‘type system view’ to the same bag of bits, and this always resolves to a ‘runtime nop’. This is IMHO the only area where C casts are ambigious, but I can see why they didn’t go for a bitcast when converting between float and int, that would definitely be confusing.

6 Likes

What’s missing in zig for that? (I admit I did not look at the standard, but zig already does many struct init that C does).

const x: i16 = 42.5;
const y: f32 = @cast(x);

y == ?

Should @cast be equivalent to @floor, @trunc, @round, or @ceil? @intFromFloat previously truncated, but I don’t see why that should be preferred over any of the others.

2 Likes

I’d love to be able to do something like this :

const std = @import("std");

pub fn main() !void {
    var data: [1024]u32 = undefined;
    @memset(&data, 1);

    // I would love if this lowers to the while
    // loop
    var i: usize = 0;
    var sum: u64 = 0;
    for (data[0..1024 :8]) |u| { // f would be @Vector(8, u32)
        sum += @reduce(.Add, u);
    }
    std.debug.print("{d}", .{sum});

    // to this :)
    i = 0;
    sum = 0;
    while (i + 8 < data.len) : (i += 8) {
        const my_vec: @Vector(8, u32) = data[i..][0..8].*;
        sum += @reduce(.Add, my_vec);
    }

    while (i < data.len) : (i += 1) {
        sum += data[i];
    }

    std.debug.print("{d}", .{sum});
}

Basically the ability to add a step [from..to:step] I think this would make writing simd code very easy, and it would make handling multi dimensional data quite nice too.

12 Likes