Skip to content

Comment on Zig: Upcoming release postponed two more weeks and lacks async functionsparent

Comments

How the heck would that even work...

In C#, they implement Await/Async by converting the function into a Class, just like when you use 'yield return'. Control flow is all over the place.

Zig is so strict about "no hidden control flow" that you can't even have destructors (code which runs when a variable goes out of scope)

Huh? Lots of language do not have guaranteed to be called at a specific time “destructors”.

Additionally, zig provides this functionality with defer and errdefer, giving you the ability to explicitly call a function at the end of scope.

Lots of language do not have guaranteed to be called at a specific time “destructors”.

Those tend to be GC languages though, where deterministic execution is not a hard requirement.

Sure, but this is also what defer is for.

There isn’t implicitly called destructors because the languages mantra is “no hidden control flow”. RAII et semantics is almost entirely hidden control flow that you just have to “know”.

Lots of people hate when they need to “just know” things to fully parse the code they’re reading.

You’re free to dislike those decisions, of course. Personally, I like the target of no hidden flow.

I have a love/hate with this.

The big problem with defer/errdefer is that I have no way to mark a function as "This function always needs a defer after it and the compiler needs to yell at me if it doesn't exist."

It's also sometimes really hard to scope your defer/errdefer correctly. You may have to twist your code inside out because defer/errdefer ends at a block scope while your variable may not (via: "break :blk varname;").

This all bites so hard for things like reference counting. The bugs ... the bugs ... <mumbles huddled up in corner>

You can `errdefer/defer if` inside the blk, then x = blk:{}; defer use(x)` outside the block.

That wasn't a request for help (I have debugged my reference counts already), but I thank you nonetheless.

You can do as you say, but that winds up being a lot of non-enforced boilerplate. And you can get subtle errors if you miss the defer at the wrong scope (ask me how I know this ... actually, better yet, please don't as it will give me flashbacks :) ). And neither the language nor compiler can help you. This contrasts with, say, Rust where reference counts or locks can be enforced by the compiler and simply never go wrong.

defer/errdefer is obvously vastly better than C. My codebase was painful in C. Zig made it tractable, but it was hardly pleasant.

Unfortunately, I really don't have a good suggestion as to what Zig should do instead. defer/errdefer is a minimum, but it's not clear what a single, better step further actually would be. Most solutions in other languages wind up with RAII and that invokes a nightmare of cascading design decisions through a programming language that Zig very much does not want to follow.

It will be interesting to see where async/await finally lands. I think that will have some component of a "slightly better" solution.

Depends on the language, and the set of automatic memory management tooling available.

Yup. The lack of destructors means that at the time there wasn't an agreed upon way to cancel an awaitable function. Async/await in zig was cool, but there's a reason why it's described as an experimental feature in the article.

Destructors don't seem to be the best place to implement cancellation for async functions: Rust async is going through a phase where the community is realizing it would be nice to have async destructors or non-cancellable async functions to avoid introducing unnecessary overhead like concurrent reclamation, reference counting, and dynamically allocated memory for things that would otherwise be done statically using structured concurrency.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.