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

Ha, I was thinking about wishing that:

fn f(foo: Foo) Bar
  • passes foo optimally by immutable reference or by copy depending on specific Foo type and target CPU
  • similarly, constructs Bar in place or copy depending on what’s optimal
  • and safety checks aliasing issues that might arise.

To generalize, I with that Zig never did an implicit copy of data for safety, and instead used a combination of safety checks for surprising behavior and an explicit copy operator.

And, look, my with just got denied, see issue #32009 on codeberg!

7 Likes
  • Ability to mix callconvs. This already works on x86_64 thanks to llvm, but not much on other archs thanks to llvm (they do offer attributes on other archs to have different callconv mainly for wine, weird). Stricter control over ABI would be nice as well, but I can live with “assume whatever LLVM does” :slight_smile:
  • Stackless coroutines (proposal exists)
  • Maybe wishful thinking, but if ranged ints pans out we could have wuffs facts
  • infer T could be nice but I can live without
  • Something needs to be done regarding usize vs uintptr_t, there’s two problems, different address spaces and pointer provenance
  • Wishful thinking, but math operations on arrays

Other than that I’m pretty content with zig as a language, all it needs is code generation improvements, more backends, as well as alternative implementations and alternative stds to explore different ideas. There might always be things that pop in head or would be nice, but this is the list that I would go with right now.

5 Likes

My wish: Linear Types


What are linear types?

If you like reading papers: “Linear types can change the world!” by Philip Wadler since 1990.

An object instance of a linear type can be used exactly one time. This prevents resource leaking, double free and other issues by having the compiler check that it is actually used exactly one time. Linear types are viral, if you include a linear type as a member in a struct or union then the new type is also linear.


Why?

Proper resource/ownership management! There is no way to open a file without closing it, you cannot double close it, the only way is to close it once for all code paths.

2 Likes

I think linear types kinda go against the status quo of zig. Resource leaks etc would be prevented with debug Io for example. I feel like concept of Io arena could be interesting as well. (It is possible to close multiple fds with close_range and io_uring for example)

3 Likes

Make it easy for numerical and scientific computing.
Finite element, CFD, meshing and graphics

1 Like

Just make @src() caller aware, not only callee based. No need to call dbgPrint(@src(),arg…) anymore.

3 Likes

Insane!! Whst happens when you make that array a million long?

Hm… right now you can decide what to do, which gives freedom.

I’m very doubtful of linear types: each time someone show an example of an API using linear types: usually opening and writing to a file is the , the author “forget” that opening, writing, closing a file all can return an error..
So are linear types really usable for “real” API ? I’m not sure..

The language problem here is that, at the time you write the code, you don’t have access to the information to make the optimal decision. There are two cases:

First, for concrete type Foo, either fn f(foo: *const Foo) or fn f(foo: Foo) might be optimal, depending on specific CPU architecture. If CPU has a lot of registers, you might pass larger structs in registers. If there are few registers, you might want to use a pointer. The conventional wisdom is that you want to pass “small” types by value, but the exact boundary of “small” depends on the target ABI.

Second, the code might be generic. Eg, ArrayList append method is:

pub fn append(self: *Self, item: T) Allocator.Error!void {

which is good for “small” Ts, but you’ll want *const T for large ones. You have to make a choice when picking the signature, and the choice will be suboptimal for some values of type parameters!


One would think that it doesn’t matter, and that we can just rely on compiler to optimize things regardless of the original form we write the code in, but that’s not so simple, because two forms come with different semantic constraints for optimizer, and, ideally, we want to have a union of constraints.

Eg, when you write *const T, you make the address of parameter significant, the T now has both the value and identity, and the compiler can’t just replace the with T, as that erases the address.

Conversely, when you write T, you claim that you need an independent, unaliased copy, so going from *const T to T generally requires copying data.

Ideally, you’d be able to express parameter passing mode where you don’t care about address of the parameter, but also don’t care whether it isn’t an independent copy of data, but rather just an alias of data stored elsewhere.

16 Likes

Not really. Linear types are about catching resource ownership errors during compile time, according to zig zen: “Compile errors are better than runtime crashes.”

1 Like

There is no need to change any API in order to use linear types. Marking the resource types as linear is enough to have the compiler check for errors. The devil is in the details of the compiler implementation that must track a lot of things such as marking each reference function parameter if the resource escapes or no.

Yes that is true. There is some “lack of control” for the programmer.

Totally agree. It’s also the direction programming languages are heading (see eg Hylo Introduction to Hylo | Hylo)

I use to pass everything by value, which only really works if LLVM is on a happy day.

Passing by value mean the optimizer can do a better job because it doesn’t have to be super conservative to work with crazy pointer aliasing. But in practice it means you end up with a lot of extra copies of stuff.

For a lot (me included) the pain is in math, anytype, loops, explicit typing.

Dear Spirit, create me:

Really Raw Expressiveness for enums, for example:

/// Indexing by [color][piecetype].
counts: [2][6]u8,

to here

counts: [Color][PieceType]u8,
4 Likes

like instead of using @intFromEnum?

more like replacing std.EnumArray

yeah. just saw there is no chance this wish will be fulfilled :slight_smile:
@intFromEnum is not a no-op, unfortunately.

A: “Hey, B. Here’s the data. Now it’s your responsibility.”
B: “Okay. I’ll obliterate it later if timeout reach.”

Months ago I want something like ownership or semantic move.

But now my mind set seems telling control it in single file data as “global object“ and use something like unwrap anywhere when using zig, it became more to “our responsibility”.

If you really want to avoid all those @intFromEnum calls, you could always create your own enum-parameterized container types with APIs that accept enums and do the conversion for you. You just have to accept you use an API rather than syntax.