The Stat struct seems to be missing uid/gid fields.
I can always get them from posix versions of stat, but that’s no good (want to scan dirs and batch stat syscalls with the new shiny async, which in my understanding should work fine with evented IO).
std.Io provides platform specific implementations such as e.g. io_uring support for Linux.
If I need both stat and std.Io evented features, I would likely need to write my own std.Id layer but also cannot use the standard abstractions (Stat). I don’t think I can batch std.linux.stat syscalls in evented.
mode is a UNIX concept, ctime is. Attributes are a windows concept. All of these are handled in std.Io, despite them being platform specific.
I don’t think that it’s a good choice to favor portability to this extend. If you do, std.Io would become a portability abstraction layer of smallest common denominator, not the fundamental IO abstraction it can and should be.
I leave this open, because while the topic is a duplicate, my problem is not that I didn’t find the linux stat impl, but that I can’t integrate it into std.Io. So the solution to the other issue does not really apply here.
This could just be comptime variants? Optionals eat memory.
Alternatively: Posix covers almost the entirety of non-Windows OS. I don’t see why Windows and POSIX functionality need to be merged into a single abstraction, making both Windows and UNIX system pay an overhead they never really benefit from (you need to discriminate over the OS anyway for non-trivial features).
I believe the point of Io is not to abstract only over the chosen concurrency / synchronicity / parralelism, but also to abstract over different implementation and / or backends. The goal here is to write reusable programs.
I don’t object to that goal. I think the question is whether the concept of uid/gid, Windows FS attributes, POSIX modes, and the many things that are not supported on all platforms should be supported by std.Io.
The problem is that specific std.Io implementations do more than to just run system calls, the hook into whatever async/conc framework implements the coordination. Because of that, you cannot simply replace something like a concrete stat implementation. That in turn means that every least common denominator decision is not just an inconvenience but it excludes functionality from participating in the std.Io ecosystem.
In this concrete example, it’s not a big deal. I can create my own IO ring and run posix/linux stat in batches. But I don’t think that this should be necessary, because Windows does not have uid/gid. Windows also does not have io_uring and that’s the std.Io implementation I use to batch stat calls.
Don’t get me wrong, an abstraction layer over various OS’ is great, I love the way how Zig did this in the past and even more so how it’s doing it now with the new Io. I just don’t think that the path of the least common denominator is the right one to take at this abstraction level. std.Io is just barely more abstract than system calls (adding the communication layer for async/conc). This does not seem the right place to add a platform abstraction that cuts off essential functionality from all linux/macos/*bsd in order to accomodate the fact that Windows doesn’t have it.
Why not using the mechanism replacing usenamespace to add a posix property to Stat?
That would be a zero cost abstraction and it would force code using it to explicitly document intend to work in posix context. It would result in clear compiler messages when trying to run on Windows. Linux specific features could then live in Stat.posix.linux or Stat.linux.
Everything that actually is platform independent would be directly in Stat.
I don’t see any downside to that approach other than the effort to implement it and the complexity of managing such interfaces. The complexity is a pain point, but if it’s ever justified, then this is probably the use case, considering how hard it is to write portable software.
Edit: I also think that it’s rather urgent to decide that soon, because breaking changes of this kind are poison for adoption. I was just about to migrate some tools from Go to Zig, looking forward to bragging to colleagues with performance improvements resulting from std.Io. I can’t do that if I have to sneak around std.Io to implement my own io_uring, Go can cheat too.
Why not the usingnamespace-replacement pattern? On a posix-like or linux platform, there is a Stat.posix/linux property. Portable code accessing it needs to comptime check for posix/linux. Non-portable code gets a compile error on Windows and runs happily on Linux/*BSD/mac.
yes, but zig makes it typed! not that you cant do that in c, and you probably know this already.
does it have to be a tagged union? the type can just change depending on the platform platform_props seems enough of an indicator that its platform specific.
also, types can be overridden in the same way std_options works.
being able to support an arbitrary platform, custom os, embedded, is (i think) a design goal.
I think they should be added to Stat and can simply be void on targets that don’t have the concept. Optionals is the wrong way to model the data because those fields cannot be null at runtime. The storage size isn’t an issue, it’s just the return type of the file system API. For long term storage applications can (and should) extract only the fields from Stat that they actually need to keep.
I do agree that they are common enough that they would benefit from being in the std Stat type. What about other information that might be very platform specific but not worth it adding to the std type. Would it make sense to be able to access the native platform specific struct? (asking in general and not just for stat)
even with what andrew just proposed, we would be branching on the Os/platform.
If you need to get more info at that point, it’s not much of a stretch top reach for the platform specific APIs, though it has the caveat of not being able to participate in any fibers/event loop/etc the Io implementation has.
Yes, and I think it would be valuable if you could participate with the std.Io instance, assuming it’s possible in the underlying impl.
I’m just asking something like
const st = doStat(io, ...);
// accessing this may cause a build failure depending on target
st.native.somethingSomethingPlatformSpecific;
The type of native would need to be determined by the std/std_options. I don’t think there’s sane way to let Io implementation to determine these types unless something like userdata pointer / type erased design would be used as mentioned by @floooh