I don’t think the language is missing anything to do this. I think there are a few other ecosystems that have support for it.
I assume you mean the failing copy on completed builds when the code folder is a mounted NTFS filesystem. The easiest way to fix that is to make a symbolic link for the .zig-cache/ folder in your project to one properly inside Linux like ~/.zig-cache once you do that it builds just fine.
Good thinking, I’ll keep that up my sleeve.
Any missing examples or unclear docs, I wouldn’t consider these a strike against Zig unless the language had been 1.0 for a year or two and had made no effort to improve this.
I think real hurdle is validating the examples in the documentation itself.
I would wish for maintainers to sort through Pull Requests.
There are 100+ all checks passing PR, that just wont get merged.
I wish for a full (debug and release modes) custom backend that can be used to enable language features such as a set of guaranteed optimizations, a sane and well defined memory model, etc, as determined by the core team.
A thing I would find cool: a IDE, maintained by a core (side) team for development in Zig only. compiling, profiler, asm view, running and debugging etc. all in one.
Coming from delphi and c#, feeling crippled often.
I must admit after finding Build-On-Save - zigtools and using it like this in my build.zig (i have no exe just a library with tests), my qualitfy of life has significantly improved.
const check = b.addTest(.{
.root_module = mod,
.test_runner = .{ .path = b.path("test_runner.zig"), .mode = .simple },
});
const checkStep = b.step("check", "Check if foo compiles");
checkStep.dependOn(&check.step);
To remain true to KISS. To keep the surface the smallest possible, no bigger.
Largely stop adding features, refine core language, keep std minimal, work on stabilization + slowly replacing llvm. I personally don’t care about breaking changes, I care much more about bloat and feature creep.
I like Odin as well but it is expressely “only for a normal 32/64 bit computer running on a OS” which isn’t what I want - C is a great language because I can use the same language for my 8 bit microcontroller, kernel code, a 64-bit game engine, a server backend, with OS/without OS, etc. Zig is poised to be that as well.
I don’t really have strong opinions on little things like syntax or this or that. I just want a sane and minimal set of “modern” features (meaning: half decent metaprogramming, slices, strings, type systems using techniques from AFTER 1970, etc). A small expressive and light language that can replace C. If I desire an ever-expanding batterys included monstrosity I am more than comfortable writing Rust (which I say lovingly, I have written 10x more Rust than Zig).
I’d love if the compiler could be imported as a library. Even better if only the codepaths required for the current platform were included in the binary. Even better still if the AST could be partially loaded and compiled ahead-of-time and stored in the binary, with only certain modules needing to be defined as regular structs like you already can in build.zig in order to complete the compilation.
My usecase: writing an interpreter loop which can also be used as a static compiler. A jump-threaded interpreter can be converted to a compiler in Zig by using @call and switching between inline and tail calls based on whether the input is compile-time. Having limited access to the Zig compiler would mean that I could write a single bytecode, get close-to-native performance, and ensure that the implementation of the interpreted and compiled versions of the runtime are precisely equal.
+1
You can sort of get the benefits of GADTs with comptime imperatively (or maybe completely and I just haven’t figured it out) - but I wish there was a more declarative way to accomplish this.
Generic function deconstruction syntax:
const a: std.ArrayList(u32) = undefined;
const _: std.ArrayList(|T|) = a;
assert(T == u32);
Based on Proposal #32099 - remove @TypeOf and anytype; introduce |T| syntax - ziglang/zig - Codeberg.org.
And ideas about #32099:
- Replace peer type parsing with
@PeerType(...). - Not evaluating the expression in
_: |T| = <expr>.
Private fields in structs, and private functions in structs that are not module level.
I’m actually a fence sitter on this one. I don’t want truly private fields & namespace functions, as it’s impossible to anticipate all the ways users will want to use your code/library, and you don’t want them sighing, if not cursing you, because they absolutely need that one piece you made private.
However, there’s great value in a standardized way of communicating what is and isn’t part of the public API, and it’d be great if there were some language-level features that standardize this and allow tooling to surface this info to users in some way (for example, maybe color private fields/functions red in docs) instead of everyone doing it in their own way in doc comments. Especially since the style guide suggests against underscore prefixes which anyone with some Python experience would default to.
With this approach, people can use the private API (internals) while still making it very clear these were not designed with users in mind, but if they find it useful - more power to them. It also helps with semantic versioning since mucking around with internals would not be considered a breaking change. You version based on your public API. And hey, if you see enough people relying on some internal, you might make it part of the public API after some consideration.
In short, a documenting feature a little more precise and friendlier to dev tools than relying on doc comments.
For me, the reason for private fields and functions in particular, is so that we can possibly have safer pointers for heap allocations that can potentially prevent use after frees from occurring in Zig. Currently, std allocators return raw pointers / slices / whatever, which can still access the memory even after the memory is freed.
The idea is that instead of a raw pointer / slice / whatever, the std allocators instead return a struct consisting of said raw pointer / slice / whatever to heap memory, and a nullable Allocator pointer field that points to the allocator that allocated the memory on the heap. When the heap memory gets freed, the allocator in the struct gets set to null. The struct then has a dereference function, which can be checked if the allocator is not null before it gets dereferenced or otherwise returns some error if null. The problem from a safety perspective of having the pointer / slice / whatever be public in the struct is that one can simply bypass all the safety restrictions and access and dereference the pointer field to the raw memory, yielding possible use after frees.
Better standard library documentation, please. Document every function and every type. Provide examples.
That’s a sensible reason. I’ve been thinking about this from a different angle: treating Zig as a stable foundation to work with, how can I build it so that my apps follow those sorts of rules?
I’m thinking:
- When I want to limit access to some unsafe internals, I’ll put that set of fields in an inner struct and name it “unsafe_internals”.
- For the occasional reviews it might be nice to use different names, maybe this should be “field names that start with unsafe_internals” so each system can be searched independently.
- Document enough of its rules/invariants that I can review each usage.
- Configure a linter that can tell me “nothing uses a field named unsafe_internals”.
- Override the linter rules with one-off “disregard this rule” exclusion comments for the valid uses. (Including but not 100% limited to the uses through the ‘real’ api.)
So my audit surface here is:
- For each release build: run the linter
- Occasionally: review/document all code using of “unsafe_internals”
Caveat: I only built a proof-of-concept of this using zlinter. I don’t have it fully reimplemented w/ 0.16.0 but I think my proof-of-concept covered enough that it won’t surprise me when I take the time for it.
replace this
@as(f32,@floatFromInt(a)) / @as(f32,@floatFromInt(b));
with this :
@cast(f32,a)/@cast(f32,b)
the casting situation is bad.
broader documentation
I mean, if the int type can safely coerce to a float then you don’t need to do the @floatFromInt().
const std = @import("std");
fn divide(numerator: u32, denominator: u32) f64 {
return @as(f64, numerator) / @as(f64, denominator);
}
pub fn main() !void {
std.debug.print("{}", .{divide(10, 3)});
}