Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

But you can't call it from synchronous rust. Zig is moving toward all sync code also using the Io interface.





Let me rephrase, you can't call it like any other function.

In Zig, a function that does IO can be called the same way whether or not it performs async operations or not. And if those async operations don't need concurrency (which Zig expresses separately to asynchronicity), then they'll run equally well on a sync Io runtime.


> In Zig, a function that does IO can be called the same way whether or not it performs async operations or not.

no, you can't, you need to pass a IO parameter


You will need to pass that for synchronous IO as well. All IO in the standard library is moving to the Io interface. Sync and async.

If I want to call a function that does asynchronous IO, I'll use:

   foo(io, ...);
If I want to call one that does synchronous IO, I'll write:

    foo(io, ...);
If I want to express that either one of the above can be run asynchronously if possible, I'll write:

    io.async(foo, .{ io, ... });
If I want to express that it must be run concurrently, then I'll write:

    try io.concurrent(foo, .{ io, ... });
Nowhere in the above do I distinguish whether or not foo does synchronous or asynchronous IO. I only mark that it does IO, by passing in a parameter of type std.Io.

what about non-io code?

What about it? It gets called without an Io parameter. Same way that a function that doesn't allocate doesn't get an allocator.

I feel like you're trying to set me up for a gotcha "see, zig does color functions because it distinguishes functions that do io and those that don't!".

And yes, that's true. Zig, at least Zig code using std, will mark functions that do Io with an Io parameter. But surely you can see how that will lead to less of a split in the ecosystem compared to sync and async rust?


This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it.

React gets around this with the context hook and which you can access implicitly if it has been injected at a higher level.

Do you know if Zig supports something of the sort?


I think (and I’m not a Zig user at anything above a hobbyist level) based on what the developers have discussed publically:

React has a ‘roughly’ functional slant to the way it does things and so needs to provide a special case ‘hook’ for a certain type of context object. Zig however is an imperative language that allows for global state (and mutable global state for that matter), which means that there is always a way to access global variable, no hook required. On the other hand, I am relatively certain (almost 100% to be honest) there can not be a context/IO , or any data/variable, passed into a function higher up the call stack and have that propagate to the lower level via implicit inclusion.


> This creates the drill-down issue we see with React props where we have to pass objects around in the call chain just so that somewhere down the line we can use it.

Oh dear God. That's hell.

Refactoring and plumbing code to change where io happens is going to be a nightmare.


> Refactoring and plumbing code to change where io happens is going to be a nightmare.

I doubt it. It's pretty rare that I want to change something deep in my code to suddenly want to do IO.

This has been a non-issue for me with Zig's approach to Allocators, and I doubt it will become an issue with Io.


It doesn't and likely never will.

This has been a non-issue for years with Allocator. I fail to see why it will be a problem with IO.


I think the view that it’s a non-issue comes down to familiarity via language usage. I am on the ‘everything explicit all the time’ team and see no issues with Allocator, or the proposed IO mechanism. But, programmers coming from other languages, particularly those with an expectation of implicitness being a semantic and syntactic feature can not envision programming without all of the alleged time saving/ergonomic ‘benefits’.

I have had multiple ‘arguments’ about the reasoning advantages, complete lack of time loss (over any useful timeframe for comparison), and long-term maintenance benefits of explicitness in language design. I have never convinced a single ‘implicit team’ dev that I’m right. Oh well, I will keep doing what I do and be fine and will support in whatever ways I can languages and language development that prioritizes explicitness.


Well it's not a "problem" in the sense that it's a blocker. But it's also not an improvement over standard async await in other languages. Which is not bad, don't get me wrong.

What do you mean by non-issue? You just accept passing it around in every function, and now passing around another param for io as well?

Or do you create a context struct and pass that around?


> You just accept passing it around in every function

In every function that needs to allocate yes. Sometimes, it'll be stored in a struct, but that's rare. And not every function needs to allocate.

> and now passing around another param for io as well?

Yes. Not everything needs to do Io.

You should try reading some idiomatic Zig code. Ghostty would be an example (as would much of the Zig standard library).


> But surely you can see how that will lead to less of a split in the ecosystem compared to sync and async rust?

not yet


Here's a problem with that:

    Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.
https://play.rust-lang.org/?version=stable&mode=debug&editio...

Right, because this would deadlock. But it seems like Zig would have the same issue. If I am running something in a evented IO system and then I try and do some blocking IO inside it then I will get a deadlock. The idea that you can write libraries that are agnostic to the asynchronous runtime seems fanciful to me beyond trivial examples.


just pass around handles like you do in zig, alright?

also: spawn_blocking for blocking code


But that's the thing, idiomatic Rust sync code almost never passes around handles, even when they need to do I/O.

You might be different, and you might start doing that in your code, but almost none of either std or 3rd party libraries will cooperate with you.

The difference with Zig is not in its capabilities, but rather in how the ecosystem around its stdlib is built.

The equivalent in Rust would be if almost all I/O functions in std would be async; granted that would be far too expensive and disruptive given how async works.


> But that's the thing, idiomatic Rust sync code almost never passes around handles, even when they need to do I/O.

Because they don't use async inside.

Zig code is passing around handles in code without io?


> Because they don't use async inside.

But they use I/O inside, and we arrive at this issue:

I'm writing async, and I need to call std::fs::read. I can't, because it blocks the thread; I could use spawn_blocking but that defeats the purpose of async. So instead I have to go look for a similar function but of the other color, probably from tokio.

In Zig, if you're writing sync, you call the standard library function for reading files. If you're writing async, you call the same library function for reading files. Then, the creator of the `io` object decides whether the whole thing will be sync or async.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: