I've been curious about Zig. I find its cross compilation story using zig cc interesting. I like its focus on simplicity instead of debugging your knowledge of the language. On the surface it looks like a better C, that isn't as complicated as Rust.
I'll admit though the syntax is a little off putting. But that is a minor complaint. I know it's not 1.0 and there are still lots to do, but I'm curious if they do more for memory safety. With companies trying to avoid starting new code in memory unsafe languages if they don't have to , I wonder if that will hurt Zig adoption. Right now it just seems like their approach is, reduce Undefined Behavior as much as possible, make the "correct" way of programming the easiest, and have array bounds on by default. Will this be enough to make the language "memory safe" enough?
I would suggest you to checkout Odin[0]. It's very similar to Zig but has much better ergonomics and probably the closest to a 'Better C' replacement in my experience. It does array bounds checking by default (which you can turn off if you choose to)
If zig includes tags or annotations (there are a few proposals in the issues tracker) and surfaces this information at an exportable zir level, it seems likely that data provenance tracking (this includes memory safety and file descriptor, socket closing, thread spawn/despawn etc) would be able to be checked by a static analysis system. If zig supports compiler hooks, then it could conceivably halt compilation unless these invariants are satisfied.
I'm not convinced that this needs to be in the type system.
> If zig supports compiler hooks, then it could conceivably halt compilation unless these invariants are satisfied.
Can you sketch out why hooks are necessary on the proposals, like 14656 ? So far I have not read a justification, what the advantage of coupling it to the compiler provides as performance gains on concrete examples. Afaik, Rust has lifetime checks decoupled to parallelize compilation and I have not seen research or a list of the possible optimizations with synthetic benchmark results.
> I'm not convinced that this needs to be in the type system.
If you want to prevent static analysis semantics becoming an incompatibilty mess like C due to 1. unknown and 2. untrackable tag semantics, then you have to provide a at least an AST to ensure annotations have known + unique semantics as I explain in https://github.com/ziglang/zig/issues/14656#issuecomment-143.... I would say that this is a primitive quasi-type check decoupled from main compilation and could be kept very flexible for research experiments by having an uri + multihash (so basically what Zig uses as package).
More concretely: Stuff to look out for is RefinedC and related projects to have more strict C semantics + how to compose those things outside the regular Zig type system.
The big advantage to coupling to the compiler is that it gives a tighter feedback loop for the developer, versus, say, statically checking as a part of CI, or forcing the dev to manually trigger static pass
At some point you would have to choose for which analysis you want unless you force the analysis onto everybody. Which means adding it into the build system. Which means separate commands.
The nice thing is that Zig conventions make things just work, because you could choose the command for your code base and third-party code would not need to care about your chosen build commands.
The "binary lsp" will give the necessary info of what has changed etc, so the info is (eventually) incremental with minimal overhead (only ipc + optional network latency).
I would guess there will always be a niche for languages that make C's overall set of trade-offs. Rust will shrink that niche, but it'll still be there. I see Zig as targeting that niche specifically: "we can strictly improve on C in a whole bunch of ways, without changing the fundamental bargain"
There is a market for statically checked c, as evidenced by the existence of something like SEL4 (though to be fair it technically checks assembly code)
It seems like statically checked zig has a chance to be strictly easier to implement relative to statically checked C, and that's I think something to shoot for, especially since it could require very little on the part of the zig developers proper.
> ...but I'm curious if they do more for memory safety. With companies trying to avoid starting new code in memory unsafe languages if they don't have to, I wonder if that will hurt Zig adoption.
I'm of the opinion that such companies or persons that would use it, are not as concerned with memory safety. Probably their use cases are more specific, limited, or narrower. Else they would likely go with more convenient GC using languages. Of which, various such languages are also compiled with optional GCs that can be turned off (Dlang, Nim, Vlang).
That, or deal with the greater complexities of adopting Rust or Ada, for primarily attempting to gain such greater memory safety. Per that part of your comment, do think the adoption will be smaller, more niche, and/or we will see it hit a lower ceiling.
Anecdotally, I really enjoy C (and Python and my Forth implementations and a few things), and Zig is 10x better for the sorts of things I like about C (some of the syntax, especially the implementation of explicit casts, is a bit verbose for my taste, but it's a small price to pay). Talking about UB and memory safety though:
- The happy path in Zig encourages memory-safe programming and has some tools (runtime checks by default, "Release-Safe" builds with those same checks) for validating reasonable behaviors.
- The (explicitly cultivated) culture around constructs like Allocators helps one be very aware of the memory they're using and what they need to free.
- Conventions of the form `try Obj.init()` and `obj.deinit()` act a lot like RAII and further emphasize the happy path in ziggish code.
- Directly contrasting the rust I write for $WORK and the zig I write for me, especially when the actual memory layout matters (e.g., pick your favorite performance metric) I don't have a substantial difference in the memory safety bugs I write in either language. Rust is actively trying to improve that story and reduce the need for unsafe blocks for atypical constraints, but IMO it's not good enough yet.
- Zig has some shocking footguns that combined with the current pre-1.0 state of code and documentation can introduce issues you probably would not expect. E.g., most meaningful uses of async frames require them to be stored somewhere, but applying the same coding techniques you would in most Zig code and adapting the docs to do so you'll write something that creates a frame and puts it somewhere. Doing so will actually work for small examples, but as soon as that frame does something with a pointer to anything in its stack it'll blow up in production because it's retaining a pointer to the old memory you copied from rather than the new place you copied it to. There are easy solutions, but without in-depth knowledge about exactly what's happening (which is hard to obtain from the current docs) it's hard to realize you would have to find a solution (admittedly, the Zig Discord is __amazing__ at quickly helping people with such issues, so it's not necessarily a huge deal since it's solvable that way now and probably solvable with better docs post-1.0).
- I've only said anything about single-threaded memory safety. Rusts compile-time reader-writer locks are next-level when working on nitty gritty concurrent code. I've hacked together some interesting parallel Zig projects, and I'm like 99.9% sure they're correct now, but it was an adventure to even get it that far, and I'm still not 100%. I haven't been actively following Zig news for a couple months, but last I heard they're planning something important with async/await to improve this story, but it's not there yet.
I'll admit though the syntax is a little off putting. But that is a minor complaint. I know it's not 1.0 and there are still lots to do, but I'm curious if they do more for memory safety. With companies trying to avoid starting new code in memory unsafe languages if they don't have to , I wonder if that will hurt Zig adoption. Right now it just seems like their approach is, reduce Undefined Behavior as much as possible, make the "correct" way of programming the easiest, and have array bounds on by default. Will this be enough to make the language "memory safe" enough?